├── .editorconfig ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .travis.yml ├── .vuepress ├── components │ ├── GitHubWrapper.vue │ ├── GithubInfos.vue │ ├── GlobalTOC.vue │ ├── Mermaid.vue │ ├── PublishInfos.vue │ ├── SvgBadge.vue │ ├── TitleInfos.vue │ ├── github-button.vue │ ├── slide.vue │ ├── swiper.vue │ └── words.vue ├── config.js ├── dist │ └── pdf │ │ ├── generate_pdf_with_toc.py │ │ └── toc.txt ├── enhanceApp.js ├── font │ └── ALIBABA-PUHUITI-REGULAR.OTF ├── plugins │ ├── export │ │ └── index.js │ └── read-time │ │ └── index.js ├── public │ ├── favicon.ico │ ├── fenix-cli │ │ └── dl.sh │ ├── images │ │ ├── Author-IcyFenix-blue.svg │ │ ├── DocLicense-CC-red.svg │ │ ├── License-Apache.svg │ │ ├── Release-v1.svg │ │ ├── ddd-arch.png │ │ ├── istio-ms.png │ │ ├── kubernetes-ms.png │ │ ├── logo-color.png │ │ ├── springcloud-ms.png │ │ ├── sshot-index.png │ │ └── sshot.jpg │ └── pdf │ │ ├── the-fenix-project.pdf │ │ └── tips.txt ├── qcloud.cdn.refresh.js ├── styles │ ├── index.styl │ └── palette.styl └── theme │ ├── components │ ├── PageEdit.vue │ ├── PageNav.vue │ └── SidebarLink.vue │ ├── index.js │ └── styles │ └── wrapper.styl ├── CHANGELOG.md ├── README.md ├── SUMMARY.md ├── appendix ├── build-script.md ├── continuous-integration.md ├── deployment-env-setup │ ├── README.md │ ├── setup-docker.md │ └── setup-kubernetes │ │ ├── README.md │ │ ├── images │ │ ├── kubernetes-initialized.png │ │ ├── kubernetes-setup-completed.png │ │ ├── rancher-add-cluster.png │ │ ├── rancher-import-cluster.png │ │ ├── rancher-import-command.png │ │ ├── rancher-import-pendding.png │ │ ├── rancher-setup-cluster.png │ │ ├── rancher-setup-cluster2.png │ │ ├── rancher-setup-success-provisioning.png │ │ └── rancher-setup-success.png │ │ ├── setup-kubeadm.md │ │ ├── setup-minikube.md │ │ └── setup-rancher.md ├── istio.md └── operation-env-setup │ ├── README.md │ ├── elk-setup.md │ └── prometheus-setup.md ├── architect-perspective └── general-architecture │ ├── README.md │ ├── api-style │ ├── README.md │ ├── images │ │ ├── Roy_Thomas_Fielding.jpg │ │ └── rpc.png │ ├── mq.md │ ├── rest.md │ └── rpc.md │ ├── concurrent │ ├── README.md │ ├── hardware-concurrent.md │ ├── process-thread-coroutine.md │ ├── synchronization.md │ └── thread-safe.md │ ├── diversion-system │ ├── README.md │ ├── cache-middleware.md │ ├── cdn.md │ ├── client-cache.md │ ├── dns-lookup.md │ ├── images │ │ ├── Circular_Buffer_Animation.gif │ │ ├── ali-cdn.png │ │ ├── cloudflare.gif │ │ ├── dns-lag.png │ │ ├── http-req.png │ │ ├── http2-con.png │ │ ├── l2-lb.png │ │ ├── l3-iptun.png │ │ ├── l3-nat.png │ │ ├── l4l7.png │ │ ├── readwrite.png │ │ ├── search.png │ │ ├── tcp-conn.png │ │ └── tmc.png │ ├── load-balancing.md │ ├── scalability.md │ └── transmission-optimization.md │ ├── scalability │ └── README.md │ ├── system-security │ ├── README.md │ ├── authentication.md │ ├── authorization.md │ ├── confidentiality.md │ ├── credentials.md │ ├── exploit.md │ ├── images │ │ ├── Basic-Authentication.png │ │ ├── hmac.png │ │ ├── jwt.png │ │ ├── sshot-ca.png │ │ └── webauthen.png │ ├── transport-security.md │ └── verification.md │ └── transaction │ ├── README.md │ ├── distributed.md │ ├── global.md │ ├── images │ ├── cap.png │ └── force-steal.png │ ├── local.md │ └── share.md ├── architecture ├── architect-framework │ ├── README.md │ ├── j2ee-base-arch.md │ ├── kubernetes-base-arch.md │ ├── serverless-arch-knative.md │ ├── serverless-arch-kubeless.md │ ├── servicemesh-lstio-arch.md │ ├── springboot-base-arch.md │ └── springcloud-base-arch.md └── architect-history │ ├── README.md │ ├── images │ ├── broke.png │ ├── coresystem.png │ ├── eventbus.png │ ├── layed-arch.png │ └── sidecar.png │ ├── microservices.md │ ├── monolithic.md │ ├── post-microservices.md │ ├── primitive-distribution.md │ ├── serverless.md │ └── soa.md ├── board.md ├── distribution ├── connect │ ├── README.md │ ├── composer.md │ ├── configuration.md │ ├── images │ │ ├── client-side-lb.png │ │ ├── gateway.png │ │ ├── proxy-lb.png │ │ ├── sd1.png │ │ └── sd2.png │ ├── load-balancing.md │ ├── service-discovery.md │ └── service-routing.md ├── consensus │ ├── README.md │ ├── gossip.md │ ├── images │ │ ├── abstract.png │ │ ├── gossip.gif │ │ ├── paxos1.png │ │ ├── paxos2.png │ │ ├── paxos3.png │ │ └── paxos4.png │ ├── paxos.md │ └── raft.md ├── observability │ ├── README.md │ ├── images │ │ ├── cncf.png │ │ ├── kibana.png │ │ ├── logging.jpg │ │ ├── mon.png │ │ ├── mtl.png │ │ ├── opentracing.png │ │ ├── pinpoint.png │ │ ├── prometheus_architecture.png │ │ └── spans.png │ ├── logging.md │ ├── metrics.md │ └── tracing.md ├── secure │ ├── README.md │ ├── service-security.md │ └── zero-trust.md └── traffic-management │ ├── README.md │ ├── exception-inject.md │ ├── failure.md │ ├── images │ ├── hop1.png │ ├── isolation-none.png │ ├── isolation-tp.png │ └── state_diagram.png │ ├── qos.md │ ├── service-downgrade.md │ ├── timeout.md │ └── traffic-control.md ├── exploration ├── guide │ └── quick-start.md └── projects │ ├── README.md │ ├── fenix-bookstore-frontend.md │ ├── images │ └── ddd-arch.png │ ├── microservice_arch_kubernetes.md │ ├── microservice_arch_springcloud.md │ ├── monolithic_arch_springboot.md │ ├── serverless_arch.md │ └── servicemesh_arch_istio.md ├── immutable-infrastructure ├── container │ ├── README.md │ ├── application-centric.md │ ├── container-build-system.md │ ├── history.md │ └── images │ │ ├── controller.png │ │ ├── docker.jpg │ │ ├── helm-hub.png │ │ ├── kubernetes.png │ │ ├── oam.png │ │ ├── pods.png │ │ ├── rolling.png │ │ └── runc.png ├── mesh │ ├── README.md │ ├── communication.md │ ├── ecosystems.md │ └── images │ │ ├── ebpf.png │ │ ├── eco.png │ │ ├── iptables.png │ │ ├── istio-arch.png │ │ ├── service-mesh-1.png │ │ ├── service-mesh-2.png │ │ ├── service-mesh-3.png │ │ ├── service-mesh-4.png │ │ ├── service-mesh-5.png │ │ ├── smi.png │ │ ├── udpa.png │ │ └── xds.png ├── msa-to-cn.md ├── network │ ├── README.md │ ├── cni.md │ ├── images │ │ ├── bridge.png │ │ ├── latency.png │ │ ├── macvlan.png │ │ ├── msg.png │ │ ├── net-provider.png │ │ ├── netfilter.png │ │ ├── throughtput.png │ │ ├── tun.png │ │ ├── veth.png │ │ ├── vlan-router.png │ │ └── vxlan.jpg │ ├── linux-vnet.md │ ├── plugin.md │ └── strategy.md ├── schedule │ ├── README.md │ ├── hardware-schedule.md │ └── images │ │ ├── 2loop.png │ │ └── context.png └── storage │ ├── README.md │ ├── csi.md │ ├── images │ ├── aws.png │ ├── csi-arch.png │ ├── csi-protects.png │ ├── flexvolume.png │ ├── pv-pvc.png │ ├── storage-arch.png │ ├── storage-class.png │ ├── types-of-mounts.png │ ├── v-pv.png │ └── volume.png │ ├── storage-evolution.md │ └── storage-plugins.md ├── introduction ├── _private │ └── cv.md ├── about-book.md ├── about-me.md ├── about-the-fenix-project.md └── images │ ├── book.png │ ├── github-brands.svg │ ├── icyfenix.jpg │ ├── icyfenix2.jpg │ ├── icyfenix3.jpg │ ├── icyfenix4.jpg │ ├── linkedin-brands.svg │ ├── mail-bulk-solid.svg │ ├── self-reproducing-automata.png │ ├── the-phoenix-project.png │ └── weibo-brands.svg ├── methodology ├── forward-msa │ ├── README.md │ ├── governance.md │ ├── granularity.md │ ├── images │ │ ├── autonomous.png │ │ ├── evolution.png │ │ ├── line.png │ │ └── ms.jpg │ ├── measurement.md │ ├── objective.md │ └── prerequest.md └── pattern │ ├── afk.md │ ├── events │ ├── README.md │ ├── cep.md │ ├── cqrs.md │ └── es.md │ └── orchestration.md ├── package-lock.json ├── package.json └── tricks ├── 2020 ├── graalvm │ ├── README.md │ ├── graal-compiler.md │ ├── graalvm-native.md │ ├── images │ │ ├── graal-hotspot.png │ │ ├── grallvm.png │ │ ├── substrate1.png │ │ └── substrate2.png │ ├── spring-over-graal.md │ ├── substratevm.md │ └── video.md └── java-crisis │ ├── images │ ├── future.png │ ├── inline.png │ ├── matrices.png │ ├── startup.png │ └── threads.png │ └── qcon.md └── 2021 ├── README.md ├── arch └── README.md ├── fenix-cli ├── README.md └── assets │ ├── 1.gif │ ├── 10.gif │ ├── 3.gif │ ├── 4.gif │ ├── 5.gif │ ├── 7.gif │ ├── 8.gif │ └── 9.gif ├── geekbang.md ├── jdkbuild └── README.md ├── k3s ├── README.md ├── apiserver │ ├── README.md │ ├── bootstrap.md │ └── env.md ├── build.md ├── kubectl │ └── README.md └── kubelet │ └── bootstrap.md └── openjdk-for-dummies ├── README.md └── images ├── build.png ├── debug.png ├── index.png └── reg.png /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Generate Github-Pages from markdown files 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | # pull_request: 7 | # branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | node-version: [14.x] 15 | 16 | steps: 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v2 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | 22 | - uses: actions/checkout@v2 23 | 24 | - name: Run Export 25 | run: | 26 | export TZ='Asia/Shanghai' 27 | npm install 28 | npm run build 29 | echo "icyfenix.cn" >> .vuepress/dist/CNAME 30 | 31 | - name: Deploy Github-Pages 32 | uses: JamesIves/github-pages-deploy-action@4.1.4 33 | with: 34 | branch: gh-pages 35 | folder: .vuepress/dist 36 | 37 | - name: Refresh CDN 38 | run: npm run deoply:refresh-cdn 39 | env: 40 | CDN_ID: ${{ secrets.CDN_ID }} 41 | CDN_KEY: ${{ secrets.CDN_KEY }} 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | .vuepress/dist/ 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # Editor directories and files 10 | .idea 11 | .vscode 12 | *.suo 13 | *.ntvs* 14 | *.njsproj 15 | *.sln 16 | .temp 17 | temp 18 | 软件架构探索:The Fenix Project.pdf 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - lts/* 4 | before_install: 5 | - export TZ='Asia/Shanghai' 6 | install: 7 | - npm install 8 | script: 9 | - npm run build 10 | - echo "icyfenix.cn" >> .vuepress/dist/CNAME 11 | # 以下为编译PDF,耗时较长,可以考虑注释掉,手工触发 12 | - echo "Starting to Export PDF..." 13 | - sudo apt-get update 14 | - sudo apt-get -y install xfonts-utils fonts-droid-fallback xfonts-intl-asian 15 | - sudo cp .vuepress/font/*.* /usr/share/fonts/truetype/ 16 | - sudo mkfontscale && mkfontdir && fc-cache 17 | - npm run export 18 | deploy: 19 | - provider: pages 20 | skip_cleanup: true 21 | local_dir: .vuepress/dist 22 | github_token: $gh_token 23 | keep_history: true 24 | target-branch: gh-pages 25 | on: 26 | branch: master 27 | - provider: script 28 | keep_history: true 29 | skip_cleanup: true 30 | script: npm run deoply:refresh-cdn 31 | on: 32 | branch: master 33 | -------------------------------------------------------------------------------- /.vuepress/components/GitHubWrapper.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /.vuepress/components/GithubInfos.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | -------------------------------------------------------------------------------- /.vuepress/components/GlobalTOC.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 111 | 112 | 145 | -------------------------------------------------------------------------------- /.vuepress/components/Mermaid.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | 23 | 35 | -------------------------------------------------------------------------------- /.vuepress/components/PublishInfos.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /.vuepress/components/SvgBadge.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 66 | 67 | 70 | -------------------------------------------------------------------------------- /.vuepress/components/TitleInfos.vue: -------------------------------------------------------------------------------- 1 | 9 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /.vuepress/components/github-button.vue: -------------------------------------------------------------------------------- 1 | 53 | -------------------------------------------------------------------------------- /.vuepress/components/slide.vue: -------------------------------------------------------------------------------- 1 | 14 | 21 | 30 | -------------------------------------------------------------------------------- /.vuepress/components/words.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 72 | 73 | 76 | -------------------------------------------------------------------------------- /.vuepress/dist/pdf/toc.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/.vuepress/dist/pdf/toc.txt -------------------------------------------------------------------------------- /.vuepress/enhanceApp.js: -------------------------------------------------------------------------------- 1 | export default ( 2 | { 3 | Vue, // VuePress 正在使用的 Vue 构造函数 4 | options, // 附加到根实例的一些选项 5 | router, // 当前应用的路由实例 6 | siteData, // 站点元数据 7 | isServer // 当前应用配置是处于 服务端渲染 或 客户端 8 | }) => { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /.vuepress/font/ALIBABA-PUHUITI-REGULAR.OTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/.vuepress/font/ALIBABA-PUHUITI-REGULAR.OTF -------------------------------------------------------------------------------- /.vuepress/plugins/read-time/index.js: -------------------------------------------------------------------------------- 1 | const filepath = require('path') 2 | const spawn = require('cross-spawn') 3 | const moment = require('moment') 4 | 5 | const globalWords = {}; 6 | 7 | module.exports = (options = {}) => ({ 8 | extendPageData($page) { 9 | const { 10 | regularPath, 11 | path, 12 | frontmatter, 13 | _strippedContent 14 | } = $page 15 | 16 | if (!_strippedContent) { 17 | return $page 18 | } 19 | 20 | if (frontmatter && frontmatter.readingTime) { 21 | $page.readingTime = frontmatter.readingTime 22 | return $page 23 | } 24 | 25 | const excludePage = options.excludes && options.excludes.some(p => { 26 | const testRegex = new RegExp(p) 27 | return testRegex.test(path) || testRegex.test(regularPath) 28 | }) 29 | 30 | if (excludePage) { 31 | return $page 32 | } 33 | 34 | function fnGetCpmisWords(str) { 35 | sLen = 0; 36 | try { 37 | //先将回车换行符做特殊处理 38 | str = str.replace(/(\r\n+|\s+| +)/g, "龘"); 39 | //处理英文字符数字,连续字母、数字、英文符号视为一个单词 40 | str = str.replace(/[\x00-\xff]/g, "m"); 41 | //合并字符m,连续字母、数字、英文符号视为一个单词 42 | str = str.replace(/m+/g, "*"); 43 | //去掉回车换行符 44 | str = str.replace(/龘+/g, ""); 45 | //返回字数 46 | sLen = str.length; 47 | } catch (e) { 48 | 49 | } 50 | return sLen; 51 | } 52 | 53 | var words = fnGetCpmisWords(_strippedContent); 54 | globalWords[regularPath] = words 55 | 56 | $page.readingTime = { 57 | words, 58 | minutes: words / 500, 59 | globalWords 60 | } 61 | 62 | function getGitLastUpdatedTimeStamp(filePath) { 63 | let lastUpdated 64 | try { 65 | lastUpdated = parseInt(spawn.sync( 66 | 'git', 67 | ['log', '-1', '--format=%at', filepath.basename(filePath)], 68 | {cwd: filepath.dirname(filePath)} 69 | ).stdout.toString('utf-8')) * 1000 70 | } catch (e) { 71 | console.error(e) 72 | } 73 | return lastUpdated 74 | } 75 | 76 | if (path === '/') { 77 | const timestamp = getGitLastUpdatedTimeStamp(".") 78 | // 受不了各个npm和浏览器版本实现不一致的问题,改用moment格式化 79 | // const options = { year: 'numeric', month: '2-digit', day: '2-digit' }; 80 | // new Date(timestamp).toLocaleDateString('zh', options); 81 | if (timestamp) { 82 | $page.siteLastUpdated = moment(new Date(timestamp)).format('YYYY-MM-DD'); 83 | console.error("编译日期:" + $page.siteLastUpdated) 84 | } else { 85 | $page.siteLastUpdated = moment().format('YYYY-MM-DD'); 86 | console.error("GIT获取更新时间出错,采用默认时间") 87 | } 88 | 89 | // 由于README.md会显示在GitHub上,设置frontmatter不好看,改为这里进行 90 | frontmatter.title = "凤凰架构:构建可靠的大型分布式系统" 91 | frontmatter.comment = false 92 | frontmatter.pageClass = "index-page-class" 93 | } 94 | return $page 95 | } 96 | }) 97 | 98 | -------------------------------------------------------------------------------- /.vuepress/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/.vuepress/public/favicon.ico -------------------------------------------------------------------------------- /.vuepress/public/fenix-cli/dl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | VERSION="$(curl -sL https://github.com/fenixsoft/fenix-cli/releases | grep -o 'releases/download/v[0-9]*.[0-9]*.[0-9]*/' | sort --version-sort | tail -1 | awk -F'/' '{ print $3}')" 5 | NAME="Fenix-CLI $VERSION" 6 | URL="https://github.com/fenixsoft/fenix-cli/releases/download/${VERSION}/fenix-cli" 7 | 8 | printf "\nDownloading %s from %s ..." "$NAME" "$URL" 9 | if ! curl -o /dev/null -sIf "$URL"; then 10 | printf "\n%s is not found, please specify a valid VERSION\n" "$URL" 11 | exit 1 12 | fi 13 | curl -fsLO "$URL" 14 | chmod +x fenix-cli 15 | mv fenix-cli /usr/bin 16 | 17 | printf "" 18 | printf "\nFenix-CLI %s Download Complete!\n" "$VERSION" 19 | printf "%s has been successfully downloaded on your system.\n" "$NAME" 20 | printf "Need more information? Visit https://github.com/fenixsoft/fenix-cli \n" -------------------------------------------------------------------------------- /.vuepress/public/images/Author-IcyFenix-blue.svg: -------------------------------------------------------------------------------- 1 | AuthorAuthorIcyFenixIcyFenix -------------------------------------------------------------------------------- /.vuepress/public/images/DocLicense-CC-red.svg: -------------------------------------------------------------------------------- 1 | Doc LicenseDoc LicenseCC 4.0CC 4.0 -------------------------------------------------------------------------------- /.vuepress/public/images/License-Apache.svg: -------------------------------------------------------------------------------- 1 | LicenseLicenseApache 2.0Apache 2.0 -------------------------------------------------------------------------------- /.vuepress/public/images/Release-v1.svg: -------------------------------------------------------------------------------- 1 | ReleaseReleasev1.0v1.0 -------------------------------------------------------------------------------- /.vuepress/public/images/ddd-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/.vuepress/public/images/ddd-arch.png -------------------------------------------------------------------------------- /.vuepress/public/images/istio-ms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/.vuepress/public/images/istio-ms.png -------------------------------------------------------------------------------- /.vuepress/public/images/kubernetes-ms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/.vuepress/public/images/kubernetes-ms.png -------------------------------------------------------------------------------- /.vuepress/public/images/logo-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/.vuepress/public/images/logo-color.png -------------------------------------------------------------------------------- /.vuepress/public/images/springcloud-ms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/.vuepress/public/images/springcloud-ms.png -------------------------------------------------------------------------------- /.vuepress/public/images/sshot-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/.vuepress/public/images/sshot-index.png -------------------------------------------------------------------------------- /.vuepress/public/images/sshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/.vuepress/public/images/sshot.jpg -------------------------------------------------------------------------------- /.vuepress/public/pdf/the-fenix-project.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/.vuepress/public/pdf/the-fenix-project.pdf -------------------------------------------------------------------------------- /.vuepress/public/pdf/tips.txt: -------------------------------------------------------------------------------- 1 | # 生成PDF耗时较长,请在必要时候执行npm run export来更新 -------------------------------------------------------------------------------- /.vuepress/qcloud.cdn.refresh.js: -------------------------------------------------------------------------------- 1 | const qcloudSDK = require('qcloud-cdn-node-sdk') 2 | 3 | const userConfig = { 4 | secretId: process.env.CDN_ID, 5 | secretKey: process.env.CDN_KEY 6 | } 7 | console.log(userConfig) 8 | qcloudSDK.config(userConfig) 9 | 10 | qcloudSDK.request('RefreshCdnDir', { 11 | 'dirs.0': 'http://icyfenix.cn' 12 | }, (res) => { 13 | console.log(res) 14 | }) 15 | -------------------------------------------------------------------------------- /.vuepress/styles/index.styl: -------------------------------------------------------------------------------- 1 | .theme-default-content:not(.custom) 2 | @media screen and (max-width: 960px) 3 | max-width: 100%; 4 | padding: 1rem; 5 | @media screen and (min-width: 961px) and (max-width: 1366px) 6 | max-width: 92%; 7 | @media screen and (min-width: 1367px) 8 | max-width: 80%; 9 | 10 | .gt-container 11 | padding-top 20px; 12 | @media screen and (max-width: 1366px) 13 | width 90%; 14 | margin-left 5%; 15 | @media screen and (min-width: 1367px) 16 | width 80%; 17 | margin-left 10%; 18 | 19 | .gt-container 20 | .gt-meta 21 | z-index 9 !important; 22 | 23 | body 24 | word-break: break-all; 25 | 26 | blockquote 27 | font-size 15px; 28 | color #555; 29 | background-color #f9f9f9; 30 | border-left 0.4rem solid #caccce; 31 | padding: 0.25rem 1rem 0.25rem 1rem; 32 | 33 | table 34 | width: 100%; 35 | display: table; 36 | 37 | img 38 | display block; 39 | 40 | figure 41 | margin 0 42 | text-align center; 43 | 44 | img 45 | display inline; 46 | 47 | figcaption 48 | p 49 | margin 0; 50 | font-size 14px; 51 | line-height 16px; 52 | 53 | p, ul, ol 54 | line-height 1.9; 55 | 56 | #app 57 | overflow: hidden; 58 | 59 | .index-page-class 60 | footer 61 | display none; 62 | 63 | .board-page-class 64 | .gt-container 65 | padding-top 0; 66 | 67 | footer 68 | display none; 69 | 70 | 71 | .badge { 72 | font-size: 10px !important; 73 | } 74 | 75 | .quote 76 | padding 5px 15px; 77 | background-color #f9f9f9; 78 | font-size 15px; 79 | color: #555; 80 | border-left: 0.4rem solid #caccce; 81 | 82 | .title 83 | font-weight bold; 84 | font-size 16px; 85 | margin-block-start 5px; 86 | margin-block-end: 0; 87 | 88 | .custom-block 89 | &.right 90 | color transparentify($textColor, 0.4) 91 | font-size 0.9rem; 92 | text-align right; 93 | 94 | .custom-block 95 | &.center 96 | text-align center; 97 | 98 | img 99 | margin-right auto; 100 | margin-left auto; 101 | 102 | .dropdown-wrapper .nav-dropdown .dropdown-item 103 | line-height: 2rem; 104 | 105 | .print-break 106 | page-break-after: always; 107 | padding-bottom 20px; 108 | 109 | .git-hub 110 | display block 111 | padding-bottom 5px 112 | a 113 | color #fff; 114 | 115 | .gt-container .gt-error 116 | color #666; 117 | font-size 14px; 118 | 119 | .github-wrapper 120 | display block; 121 | 122 | .gt-container .gt-error:after 123 | content: ",无法访问GitHub,评论功能不能使用。你需要科学上网" 124 | 125 | .only-for-printer 126 | display none 127 | 128 | @media print 129 | .navbar 130 | .page-edit 131 | .page-nav 132 | .not-print 133 | #vuepress-plugin-comment 134 | display none !important; 135 | 136 | div[class*=language-] 137 | pre 138 | div[class*=language-] 139 | pre[class*=language-] 140 | box-shadow: inset 0 0 0 1000px #000; 141 | white-space: pre-wrap; 142 | word-wrap: break-word; 143 | 144 | .token.comment 145 | .token.block-comment 146 | .token.prolog 147 | .token.doctype 148 | .token.cdata 149 | color: #eee; 150 | 151 | .custom-block.tip 152 | .quote 153 | blockquote 154 | box-shadow: inset 0 0 0 1000px #f3f5f7; 155 | 156 | .only-for-printer 157 | display block 158 | -------------------------------------------------------------------------------- /.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | // 颜色 2 | $accentColor = #3884fe 3 | $textColor = #2c3e50 4 | $borderColor = #eaecef 5 | $codeBgColor = #282c34 6 | $arrowBgColor = #ccc 7 | $badgeTipColor = #42b983 8 | $badgeWarningColor = darken(#ffe564, 35%) 9 | $badgeErrorColor = #DA5961 10 | 11 | // 布局 12 | $navbarHeight = 3.6rem 13 | $sidebarWidth = 20rem 14 | $contentWidth = 80% 15 | $homePageWidth = 100% 16 | 17 | // 响应式变化点 18 | $MQNarrow = 959px 19 | $MQMobile = 719px 20 | $MQMobileNarrow = 419px 21 | -------------------------------------------------------------------------------- /.vuepress/theme/components/PageNav.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 128 | 129 | 149 | -------------------------------------------------------------------------------- /.vuepress/theme/components/SidebarLink.vue: -------------------------------------------------------------------------------- 1 | 107 | 108 | 143 | -------------------------------------------------------------------------------- /.vuepress/theme/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extend: '@vuepress/theme-default' 3 | } 4 | -------------------------------------------------------------------------------- /.vuepress/theme/styles/wrapper.styl: -------------------------------------------------------------------------------- 1 | $wrapper 2 | max-width $contentWidth 3 | margin 0 auto 4 | padding 2rem 2.5rem 5 | @media (max-width: $MQNarrow) 6 | padding 2rem 7 | @media (max-width: $MQMobileNarrow) 8 | padding 1.5rem 9 | 10 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | --- 2 | comment: false 3 | permalink: /summary 4 | --- 5 | 6 | # 目录 7 | 8 | :::tip 数据统计 9 | 列入目录文章 篇,实际文章总数 篇(含目录外文章), 10 | 合计总字数 字,最后更新日期 。 11 | ::: 12 | 13 | Loading... 14 | -------------------------------------------------------------------------------- /appendix/build-script.md: -------------------------------------------------------------------------------- 1 | # 构建发布脚本 2 | 3 | -------------------------------------------------------------------------------- /appendix/continuous-integration.md: -------------------------------------------------------------------------------- 1 | # 持续集成 2 | 3 | -------------------------------------------------------------------------------- /appendix/deployment-env-setup/README.md: -------------------------------------------------------------------------------- 1 | # 部署环境 2 | 3 | 这一部分介绍了 Docker 容器环境和 Kubernetes 集群环境的 Step-By-Step 的安装流程。加入开发与运维的环境依赖这些内容,严格来讲并不符合本文档的主题,但对于刚刚接触这个领域的读者,这些内容确实有一定的复杂性,为避免新人受到不必要的打击,笔者还是在附录中加入这部分的内容,如对这部分已有了解的读者,完全可以略过。 4 | -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/README.md: -------------------------------------------------------------------------------- 1 | # 部署 Kubernetes 集群 2 | 3 | Kubernetes 是一个由 Google 发起的开源自动化部署,缩放,以及容器化管理应用程序的容器编排系统。 4 | 5 | 部署 Kubernetes 曾经是一件相当麻烦的事情,早期版本中,Kubelet、Api-Server、Etcd、Controller-Manager 等每一个组件都需要自己单独去部署,还要创建自签名证书来保证各个组件之间的网络访问。但程序员大概是最爱与麻烦做斗争的群体,随着 Kubernetes 的后续版本不断改进(如提供了自动生成证书、Api-Server 等组件改为默认静态 Pod 部署方式),使得部署和管理 Kubernetes 集群正在变得越来越容易起来。目前主流的方式大致有: 6 | 7 | - [使用 Kubeadm 部署 Kubernetes 集群](setup-kubeadm.md) 8 | - [使用 Rancher 部署、管理 Kubernetes 集群](setup-rancher.md)(其他如 KubeSphere 等在 Kubernetes 基础上构建的工具均归入此类) 9 | - [使用 Minikube 在本地单节点部署 Kubernetes 集群](setup-minikube.md)(其他如 Microk8s 等本地环境的工具均归入此类) 10 | 11 | 以上集中部署方式都有很明显的针对性,个人开发环境以 Minikube 最简单,生产环境以 Rancher 最简单,在云原生环境中,自然是使用环境提供的相应工具。不过笔者推荐首次接触 Kubernetes 的同学最好还是选择 Kubeadm 来部署,毕竟这是官方提供的集群管理工具,是相对更底层、基础的方式,充分熟悉了之后再接触其他简化的方式会快速融会贯通。 以上部署方式无需全部阅读,根据自己环境的情况选择其一即可。 12 | -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/images/kubernetes-initialized.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/appendix/deployment-env-setup/setup-kubernetes/images/kubernetes-initialized.png -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/images/kubernetes-setup-completed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/appendix/deployment-env-setup/setup-kubernetes/images/kubernetes-setup-completed.png -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/images/rancher-add-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/appendix/deployment-env-setup/setup-kubernetes/images/rancher-add-cluster.png -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/images/rancher-import-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/appendix/deployment-env-setup/setup-kubernetes/images/rancher-import-cluster.png -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/images/rancher-import-command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/appendix/deployment-env-setup/setup-kubernetes/images/rancher-import-command.png -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/images/rancher-import-pendding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/appendix/deployment-env-setup/setup-kubernetes/images/rancher-import-pendding.png -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/images/rancher-setup-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/appendix/deployment-env-setup/setup-kubernetes/images/rancher-setup-cluster.png -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/images/rancher-setup-cluster2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/appendix/deployment-env-setup/setup-kubernetes/images/rancher-setup-cluster2.png -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/images/rancher-setup-success-provisioning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/appendix/deployment-env-setup/setup-kubernetes/images/rancher-setup-success-provisioning.png -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/images/rancher-setup-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/appendix/deployment-env-setup/setup-kubernetes/images/rancher-setup-success.png -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/setup-minikube.md: -------------------------------------------------------------------------------- 1 | # 使用 Minikube 部署 2 | 3 | Minikube 是 Kubernetes 官方提供的专门针对本地单节点集群的 Kubernetes 集群管理工具。针对本地环境对 Kubernetes 使用有一定的简化和针对性的补强。这里简要介绍其安装过程 4 | 5 | ## 安装 Minikube 6 | 7 | Minikube 是一个单文件的二进制包,安装十分简单,在已经完成[Docker 安装](../setup-docker.md)的前提下,使用以下命令可以下载并安装最新版的 Minikube。 8 | 9 | ```bash 10 | $ curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/ 11 | ``` 12 | 13 | ## 安装 Kubectl 工具 14 | 15 | Minikube 中很多提供了许多子命令以代替 Kubectl 的功能,安装 Minikube 时并不会一并安装 Kubectl。但是 Kubectl 作为集群管理的命令行,要了解 Kubernetes 是无论如何绕不过去的,通过以下命令可以独立安装 Kubectl 工具。 16 | 17 | ```bash 18 | $ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/ 19 | ``` 20 | 21 | ## 启动 Kubernetes 集群 22 | 23 | 有了 Minikube,通过 start 子命令就可以一键部署和启动 Kubernetes 集群了,具体命令如下: 24 | 25 | ```bash 26 | $ minikube start --iso-url=https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/iso/minikube-v1.6.0.iso 27 | --registry-mirror=https://registry.docker-cn.com 28 | --image-mirror-country=cn 29 | --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers 30 | --vm-driver=none 31 | --memory=4096 32 | ``` 33 | 34 | 以上命令中,明确要求 Minikube 从指定的地址下载虚拟机镜像、Kubernetes 各个服务 Pod 的 Docker 镜像,并指定了使用 Docker 官方节点作为国内的镜像加速服务。 35 | 36 | “vm-drvier”参数是指 Minikube 所采用的虚拟机,根据不同操作系统,不同的虚拟机可以有以下选项: 37 | 38 | | 操作系统 | 支持虚拟机 | 参数值 | 39 | | :------- | :---------------- | :--------- | 40 | | Windows | Hyper-V | hyperv | 41 | | Windows | VirtualBox | virtualbox | 42 | | Linux | KVM | kvm2 | 43 | | Linux | VirtualBox | virtualbox | 44 | | MacOS | HyperKit | hyperkit | 45 | | MacOS | VirtualBox | virtualbox | 46 | | MacOS | Parallels Desktop | parallels | 47 | | MacOS | VMware Fusion | vmware | 48 | 49 | 特别需要提一下的是如果读者使用的并非物理机器,而是云主机环境——现在流行将其称为“裸金属”(Bare Metal)服务器,那在上面很可能是无法再部署虚拟机环境的,这时候应该将 vm-drvier 参数设为 none。也可以使用以下命令设置虚拟机驱动的默认值: 50 | 51 | ```bash 52 | $ minikube config set vm-driver none 53 | ``` 54 | 55 | 至此,整个 Kubernetes 就一键启动完毕了。其他工作,如命令行的自动补全,可参考使用[Kubeadm 安装 Kubernetes 集群](setup-kubeadm.md)中相关内容。 56 | -------------------------------------------------------------------------------- /appendix/deployment-env-setup/setup-kubernetes/setup-rancher.md: -------------------------------------------------------------------------------- 1 | # 使用 Rancher 部署 2 | 3 | Rancher 是在 Kubernetes 更上层的管理框架,Rancher 是图形化的,有着比较傻瓜式的操作,只有少量一两处地方(如导入集群)需要用到 Kubernetes 命令行。也由于它提供了一系列容器模版、应用商店等的高层功能,使得要在 Kubernetes 上部署一个新应用,简化到甚至只需要点几下鼠标即可,因此用户们都爱使用它。 4 | 5 | Rancher 还推出了 RancherOS(极致精简专为容器定制的 Linux,尤其适合边缘计算环境)、K3S(Kubernetes as a Service,5 Less Than K8S,一个大约只有 40MB,可以运行在 x86 和 ARM 架构上的极小型 Kubernetes 发行版)这样的定制产品,用以在用户心中暗示、强化比 K8S 更小、更简单、更易用的主观印象。 6 | 7 | 不过也由于 Rancher 入门容易,基础性的应用需求解决起来很方便,也导致了不少人一开始使用它之后,就陷入了先入为主的印象,后期再接触 Kubernetes 时,便觉得学习曲线特别陡峭,反而限制了某些用户对底层问题的进一步深入。 8 | 9 | 在本文中,笔者以截图为主,展示如何使用 Rancher 来导入或者创建 Kubernetes 集群的过程。 10 | 11 | ## 安装 Rancher 12 | 13 | 前置条件:已经[安装好 Docker](../setup-docker.md)。 14 | 15 | 使用 Docker 执行 Rancher 镜像,执行以下命令即可: 16 | 17 | ```bash 18 | $ sudo docker run -d --restart=unless-stopped -p 8080:80 -p 8443:443 rancher/rancher 19 | ``` 20 | 21 | ## 使用 Rancher 管理现有 Kubernetes 集群 22 | 23 | 前置条件:已经[安装好了 Kubernetes 集群](setup-kubeadm.md)。 24 | 25 | 使用 Rancher 的导入功能将已部署的 Kubernetes 集群纳入其管理。登陆 Rancher 主界面(首次登陆会要求设置 admin 密码和 Rancher 在集群中可访问的路径,后者尤其不能乱设,否则 Kubernetes 无法访问到 Rancher 会一直处于 Pending 等待状态)之后,点击右上角的 Add Cluster,然后有下面几个添加集群的选择: 26 | 27 | :::center 28 | ![](./images/rancher-add-cluster.png) 29 | ::: 30 | 31 | - 要从某台机器中新安装 Kubernetes 集群选择“From existing nodes \(Custom\)” 32 | - 要导入某个已经安装好的 Kubernetes 集群选择“Import an existing cluster” 33 | - 要从各种云服务商的 RKE(Rancher Kubernetes Engine)环境中创建,就选择下面那排厂商的按钮,没有的话(譬如国内的阿里云之类的),请先到 Tools->Driver 中安装对应云服务厂商的驱动。 34 | 35 | 这里选择“Import an existing cluster”,然后给集群起个名字以便区分(由于 Rancher 支持多集群管理,所以集群得有个名字以示区别),之后就看见这个界面: 36 | :::center 37 | ![](./images/rancher-import-cluster.png) 38 | ::: 39 | Rancher 自动生成了加入集群的命令,这行命令其实就是部署一个运行在 Kubernetes 中的代理(Agent),在 Kubernetes 的命令行中执行以上自动生成的命令。 40 | 41 | 最后那条命令意思是怕由于部署的 Rancher 服务没有申请 SSL 证书,导致 HTTPS 域名验证过不去,kubectl 下载不下来 yaml。如果你的 Rancher 部署在已经申请了证书的 HTTPS 地址上那可以用前面的,否则还是直接用 curl --insecure 命令来绕过 HTTPS 证书查验吧,譬如以下命令所示: 42 | 43 | ```bash 44 | $ curl --insecure -sfL https://localhost:8443/v3/import/vgkj5tzphj9vzg6l57krdc9gfc4b4zsfp4l9prrf6sb7z9d2wvbhb5.yaml | kubectl apply -f - 45 | ``` 46 | 47 | 多说一句,用哪条命令安装的 Agent 只决定了 yaml 文件是如何下载获得的,对后续其他事情是毫无影响的,所以怎么简单怎么来,别折腾。 48 | 49 | 执行结果类似如下所示,一堆 secret、deployment、daementset 创建成功,就代表顺利完成了: 50 | :::center 51 | ![](./images/rancher-import-command.png) 52 | ::: 53 | 然后回到 Rancher 网页,点击界面上的“Done”按钮。可以看到集群正处于 Pending 状态: 54 | :::center 55 | ![](./images/rancher-import-pendding.png) 56 | ::: 57 | 如果 Agent 成功到达 Running 状态的话,这里也会很快就变成 Waiting 状态,然后再变为 Active 状态,导入工作即宣告胜利结束。 58 | 59 | 而如果一直持续 Pending 状态,说明安装的 Agent 运行失败。典型的原因是无法访问到 Rancher 的服务器,这时可以通过 kubectl logs 命令查看一下 cattle-cluster-agent-xxx 的日志,通常会看见"XXX is not accessible",其中的 XXX 是 Rancher 第一次进入时跟你确认过的访问地址,假如你乱填了,或者该地址被防火墙挡掉,又或者因为证书限制等其他原因导致 Agent 无法访问,Rancher 就会一直 Pending。 60 | 61 | 最后再提一句,Rancher 与 Kubernetes 集群之间是被动链接的,即由 Kubernetes 去主动找 Rancher,这意味着部署在外网的 Rancher,可以无障碍地管理处于内网(譬如 NAT 后)的 Kubernetes 集群,这对于大量没有公网 IP 的集群来说是很方便的事情。 62 | 63 | ## 使用 Rancher 创建 Kubernetes 集群 64 | 65 | 也可以直接使用 Rancher 直接在裸金属服务器上创建 Kubernetes 集群,此时在添加集群中选择 From existing nodes \(Custom\),在自定义界面中,设置要安装的集群名称、Kubernetes 版本、CNI 网络驱动、私有镜像库以及其他一些集群的参数选项。 66 | 67 | :::center 68 | ![](./images/rancher-setup-cluster.png) 69 | ::: 70 | 71 | 下一步确认该主机在 Kubernetes 中扮演的角色,每台主机可以扮演多个角色。但至少要保证每个集群都有一个 Etcd 角色、一个 Control 角色、一个 Worker 角色。 72 | 73 | :::center 74 | ![](./images/rancher-setup-cluster2.png) 75 | ::: 76 | 77 | 复制生成的命令,在要安装集群的每一台主机的 SSH 中执行。此时 Docker 会下载运行 Rancher 的 Agent 镜像,当执行成功后,Rancher 界面会有提示新主机注册成功。 78 | 79 | :::center 80 | ![](./images/rancher-setup-success.png) 81 | ::: 82 | 83 | 点击完成,将会在集群列表中看见正在 Provisioning 的新集群,稍后将变为 Active 状态. 84 | 85 | :::center 86 | ![](./images/rancher-setup-success-provisioning.png) 87 | ::: 88 | 89 | 安装完成后你就可以在 Rancher 的图形界面管理 Kubernetes 集群了,如果还需要在命令行中工作,kubectl、kubeadm 等工具是没有安装的,可参考“[使用 Kubeadm 部署 Kubernetes 集群](setup-kubeadm)”的内容安装使用。 90 | -------------------------------------------------------------------------------- /appendix/istio.md: -------------------------------------------------------------------------------- 1 | # 部署 Istio 2 | 3 | 在 Istio 官方文档上提供了[通过 Istioctl 安装](https://istio.io/latest/zh/docs/setup/install/istioctl/)、[通过 Helm 安装](https://istio.io/latest/zh/docs/setup/install/helm/)和[通过 Operator 安装](https://istio.io/latest/zh/docs/setup/install/standalone-operator/)三种方式,其中,基于 Helm 方式在 Istio 1.5 版之后已被废弃,Operator 方式目前仍处于实验阶段,本文选择采用 Istioctl 方式进行安装。 4 | 5 | ## 获取 Istio 6 | 7 | 首先访问[Istio release](https://github.com/istio/istio/releases/tag/1.7.3)页面下载与你操作系统匹配的安装文件。在 macOS 或 Linux 系统中,也可以通过以下命令直接下载最新版本的 Istio: 8 | 9 | ```bash 10 | $ curl -L https://istio.io/downloadIstio | sh - 11 | ``` 12 | 13 | 解压后,安装目录包括以下内容: 14 | 15 | | 目录 | 包含内容 | 16 | | --------- | ---------------------------------------------------------- | 17 | | `bin` | 包含 istioctl 的客户端文件 | 18 | | `install` | 包含 Consul、GCP 和 Kubernetes 平台的 Istio 安装脚本和文件 | 19 | | `samples` | 包含示例应用程序 | 20 | | `tools` | 包含用于性能测试和在本地机器上进行测试的脚本 | 21 | 22 | 将`bin`目录下的`istioctl`客户端路径增加到`path`环境变量中,macOS 或 Linux 系统的增加方式如下: 23 | 24 | ```bash 25 | $ export PATH=$PWD/bin:$PATH 26 | ``` 27 | 28 | 如果你在使用 bash 或 ZSH 的话,可以选择启动[Auto Completion Option](https://istio.io/latest/zh/docs/ops/diagnostic-tools/istioctl#enabling-auto-completion)。 29 | 30 | ## 默认安装 Istio 31 | 32 | 部署 Istio,最简单的方式是安装 `default` 配置文件,直接使用以下命令即可: 33 | 34 | ```bash 35 | $ istioctl manifest install 36 | ``` 37 | 38 | 此命令将在你的 Kubernetes 集群上安装`default`配置文件。`default`配置文件建立生产环境的良好起点,这与旨在评估广泛的 Istio 功能特性的较大的`demo`配置文件不同。各种不同配置文件之间的差异如下表所示: 39 | 40 | | | default | demo | minimal | sds | 41 | | ------------------------ | ------- | ---- | ------- | --- | 42 | | 核心组件 | | | | | 43 | | `istio-citadel` | X | X | | X | 44 | | `istio-egressgateway` | | X | | | 45 | | `istio-galley` | X | X | | X | 46 | | `istio-ingressgateway` | X | X | | X | 47 | | `istio-nodeagent` | | | | X | 48 | | `istio-pilot` | X | X | X | X | 49 | | `istio-policy` | X | X | | X | 50 | | `istio-sidecar-injector` | X | X | | X | 51 | | `istio-telemetry` | X | X | | X | 52 | | 插件 | | | | | 53 | | `grafana` | | X | | | 54 | | `istio-tracing` | | X | | | 55 | | `kiali` | | X | | | 56 | | `prometheus` | X | X | | X | 57 | 58 | 安装`default`配置文件后,如果需要其他组件或者插件,可以进行独立安装。譬如要在`default`配置文件之上启用 Grafana Dashboard,用下面的命令设置`addonComponents.grafana.enabled`参数即可: 59 | 60 | ```bash 61 | $ istioctl manifest install --set addonComponents.grafana.enabled=true 62 | ``` 63 | 64 | ## 安装 demo 配置 65 | 66 | `demo`这个词语可能会让使用者产生误解,其实 Istio 的`demo`配置是默认安装所有组件的全功能配置,从上面表格中配置与组件的对应情况中可以印证这一点。你可以使用以下`istioctl`命令来列出 Istio 配置文件名称: 67 | 68 | ```bash 69 | $ istioctl profile list 70 | Istio configuration profiles: 71 | remote 72 | separate 73 | default 74 | demo 75 | empty 76 | minimal 77 | ``` 78 | 79 | 通过在命令行上设置配置文件名称安装其他 Istio 配置文件到群集中,使用以下命令安装`demo`配置文件: 80 | 81 | ```bash 82 | $ istioctl manifest install --set profile=demo 83 | ``` 84 | 85 | ## 验证安装成功 86 | 87 | 你可以使用`verify-install`命令检查 Istio 安装是否成功,它将集群上的安装与你指定的清单进行比较。 88 | 89 | 如果未在部署之前生成清单,请运行以下命令以现在生成它: 90 | 91 | ```bash 92 | $ istioctl manifest generate > $HOME/generated-manifest.yaml 93 | ``` 94 | 95 | 然后运行以下`verify-install`命令以查看安装是否成功: 96 | 97 | ```bash 98 | $ istioctl verify-install -f $HOME/generated-manifest.yaml 99 | ``` 100 | 101 | ## 卸载 Istio 102 | 103 | 可以使用以下命令来卸载 Istio: 104 | 105 | ```bash 106 | $ istioctl manifest generate | kubectl delete -f - 107 | ``` 108 | -------------------------------------------------------------------------------- /appendix/operation-env-setup/README.md: -------------------------------------------------------------------------------- 1 | # 运维环境 2 | 3 | -------------------------------------------------------------------------------- /appendix/operation-env-setup/prometheus-setup.md: -------------------------------------------------------------------------------- 1 | # 部署 Prometheus 2 | 3 | 如果要手动在 Kubernetes 中处理安装 Prometheus 的每一个细节还是挺麻烦的,在官方的[Kube-Prometheus](https://github.com/prometheus-operator/kube-prometheus)项目里提供了明确的操作步骤。不过,如果只是通过 Prometheus Operator 的 Bundle 包安装 Prometheus 则非常简单。 4 | 5 | 首先从以下地址中获取 Prometheus Operator 的源码: 6 | 7 | ```bash 8 | $ git clone https://github.com/prometheus-operator/prometheus-operator.git 9 | ``` 10 | 11 | 安装里面的`bundle.yaml`,然后就完成了: 12 | 13 | ```bash 14 | $ kubectl apply -f bundle.yaml 15 | ``` 16 | 17 | 卸载时,同样根据`bundle.yaml`删除即可: 18 | 19 | ```bash 20 | $ kubectl delete -f bundle.yaml 21 | ``` 22 | -------------------------------------------------------------------------------- /architect-perspective/general-architecture/README.md: -------------------------------------------------------------------------------- 1 | # 架构的普适问题 2 | 3 | “设计者的视角”主要讲述作为一个架构师,你应该在做架构设计时思考哪些问题,有哪些主流的解决方案和行业标准做法,各种方案有什么优点或者缺点,不同的解决方法会带来什么不同的影响,等等。“架构”总是充满权衡博弈的,如果一件事情只有好处或者只有坏处,没有利弊优劣上的选择,也就无需架构师去做技术决策了。你所做的一个决定,可能关系到未来的系统在功能、质量属性上的高低,也关系着团队的成员工作、成长中的幸福感,请多思多想,慎重决定! 4 | 5 | 本章内容始终以业界标准方案和博弈为主线,代码怎样写、工具怎样用并不是我们讨论的主题。即使有部分内容会涉及到一些具体工具、类库的使用片段,这些代码也不足以成为它们的应用指南,起码不足以让一个完全不了解该工具的人学会如何使用,而是笔者用于说清楚某种解决方案的途径,仅此而已。 6 | 7 | “架构的普适问题”部分,会讨论与架构风格无关或者关系不太密切的通用性话题,这些话题对于任何一个系统设计者来说都可能涉及到,是针对具体问题“如何解决”、“如何实现”方面的务实讨论。在“设计方法论”部分,会针对“系统在开发之前应该进行哪些思考”进行的务虚讨论。 -------------------------------------------------------------------------------- /architect-perspective/general-architecture/api-style/README.md: -------------------------------------------------------------------------------- 1 | # 访问远程服务 2 | 3 | 远程服务将计算机程序的工作范围从单机扩展到网络,从本地延伸至远程,是构建分布式系统的首要基础。而远程服务又不仅仅是为了分布式系统服务的,在网络时代,浏览器、移动设备、桌面应用和服务端的程序,普遍都有跟其他设备交互的需求,所以今天已经很难找到没有开发和使用过远程服务的程序员了,但是没有正确理解远程服务的程序员却仍比比皆是。 4 | -------------------------------------------------------------------------------- /architect-perspective/general-architecture/api-style/images/Roy_Thomas_Fielding.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/api-style/images/Roy_Thomas_Fielding.jpg -------------------------------------------------------------------------------- /architect-perspective/general-architecture/api-style/images/rpc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/api-style/images/rpc.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/api-style/mq.md: -------------------------------------------------------------------------------- 1 | # 异步服务调用 2 | 3 | -------------------------------------------------------------------------------- /architect-perspective/general-architecture/concurrent/README.md: -------------------------------------------------------------------------------- 1 | # 高效并发 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /architect-perspective/general-architecture/concurrent/hardware-concurrent.md: -------------------------------------------------------------------------------- 1 | # 硬件并发机制 -------------------------------------------------------------------------------- /architect-perspective/general-architecture/concurrent/process-thread-coroutine.md: -------------------------------------------------------------------------------- 1 | # 进程、线程与协程 -------------------------------------------------------------------------------- /architect-perspective/general-architecture/concurrent/synchronization.md: -------------------------------------------------------------------------------- 1 | # 同步机制 2 | 3 | ## 阻塞同步 4 | 5 | ### 锁的属性 6 | 7 | #### 公平性 8 | 9 | #### 互斥性 10 | 11 | #### 可重入性 12 | 13 | 14 | 15 | ## 非阻塞同步 16 | 17 | 18 | 19 | ## 无同步机制 -------------------------------------------------------------------------------- /architect-perspective/general-architecture/concurrent/thread-safe.md: -------------------------------------------------------------------------------- 1 | # 线程安全 -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/README.md: -------------------------------------------------------------------------------- 1 | # 透明多级分流系统 2 | 3 | :::tip 奥卡姆剃刀原则 4 | 5 | Entities should not be multiplied without necessity 6 | 7 | 如无必要,勿增实体 8 | 9 | :::right 10 | 11 | —— [Occam's Razor](https://en.wikipedia.org/wiki/Occam%27s_razor),[William of Ockham](https://en.wikipedia.org/wiki/William_of_Ockham) 12 | 13 | ::: 14 | 15 | 现代的企业级或互联网系统,“分流”是必须要考虑的设计,分流所使用手段数量之多、涉及场景之广,可能连它的开发者本身都未必能全部意识到。这听起来似乎并不合理,但笔者认为这恰好是优秀架构设计的一种体现,“分布广阔”源于“多级”,“意识不到”谓之“透明”,也即本章我们要讨论的主题“**透明多级分流系统**”(Transparent Multilevel Diversion System, “透明多级分流系统”这个词是笔者自己创造的,业内通常只提“Transparent Multilevel Cache”,但我们这里谈的并不仅仅涉及到缓存)的来由。 16 | 17 | 在用户使用信息系统的过程中,请求从浏览器出发,在域名服务器的指引下找到系统的入口,经过网关、负载均衡器、缓存、服务集群等一系列设施,最后触及到末端存储于数据库服务器中的信息,然后逐级返回到用户的浏览器之中。这其中要经过很多技术部件。作为系统的设计者,我们应该意识到不同的设施、部件在系统中有各自不同的价值。 18 | 19 | - 有一些部件位于客户端或网络的边缘,能够迅速响应用户的请求,避免给后方的 I/O 与 CPU 带来压力,典型如本地缓存、内容分发网络、反向代理等。 20 | - 有一些部件的处理能力能够线性拓展,易于伸缩,可以使用较小的代价堆叠机器来获得与用户数量相匹配的并发性能,应尽量作为业务逻辑的主要载体,典型如集群中能够自动扩缩的服务节点。 21 | - 有一些部件稳定服务对系统运行有全局性的影响,要时刻保持着容错备份,维护着高可用性,典型如服务注册中心、配置中心。 22 | - 有一些设施是天生的单点部件,只能依靠升级机器本身的网络、存储和运算性能来提升处理能力,如位于系统入口的路由、网关或者负载均衡器(它们都可以做集群,但一次网络请求中无可避免至少有一个是单点的部件)、位于请求调用链末端的传统关系数据库等,都是典型的容易形成单点部件。 23 | 24 | 对系统进行流量规划时,我们应该充分理解这些部件的价值差异,有两条简单、普适的原则能指导我们进行设计: 25 | 26 | - 第一条原则是尽可能减少单点部件,如果某些单点是无可避免的,则应尽最大限度减少到达单点部件的流量。在系统中往往会有多个部件能够处理、响应用户请求,譬如要获取一张存储在数据库的用户头像图片,浏览器缓存、内容分发网络、反向代理、Web 服务器、文件服务器、数据库都可能提供这张图片。恰如其分地引导请求分流至最合适的组件中,避免绝大多数流量汇集到单点部件(如数据库),同时依然能够在绝大多数时候保证处理结果的准确性,使单点系统在出现故障时自动而迅速地实施补救措施,这便是系统架构中多级分流的意义。 27 | - 另一条更关键的原则是奥卡姆剃刀原则。作为一名架构设计者,你应对多级分流的手段有全面的理解与充分的准备,同时清晰地意识到这些设施并不是越多越好。在实际构建系统时,你应当在有明确需求、真正必要的时候再去考虑部署它们。不是每一个系统都要追求高并发、高可用的,根据系统的用户量、峰值流量和团队本身的技术与运维能力来考虑如何部署这些设施才是合理的做法,在能满足需求的前提下,**最简单的系统就是最好的系统**。 28 | 29 | 本章,笔者将会根据流量从客户端发出到服务端处理这个过程里,所流经的与功能无关的技术部件为线索,解析这里面每个部件的透明工作原理与起到的分流作用。这节所讲述的客户端缓存、域名服务器、传输链路、内容分发网络、负载均衡器、服务端缓存,都是为了达成“透明分流”这个目标所采用的工具与手段,高可用架构、高并发则是通过“透明分流”所获得的价值。 30 | -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/dns-lookup.md: -------------------------------------------------------------------------------- 1 | # 域名解析 2 | 3 | ::: tip 域名缓存(DNS Lookup) 4 | 5 | DNS 也许是全世界最大、使用最频繁的信息查询系统,如果没有适当的分流机制,DNS 将会成为整个网络的瓶颈。 6 | 7 | ::: 8 | 9 | 大家都知道 DNS 的作用是将便于人类理解的域名地址转换为便于计算机处理的 IP 地址,也许你会觉得好笑:笔者在接触计算机网络的开头一段不短的时间里面,都把 DNS 想像成一个部署在全世界某个神秘机房中的大型电话本式的翻译服务。后来,当笔者第一次了解到 DNS 的工作原理,并得知世界根域名服务器的 ZONE 文件只有 2MB 大小,甚至可以打印出来物理备份的时候,对 DNS 系统的设计是非常惊叹的。 10 | 11 | 域名解析对于大多数信息系统,尤其是对于基于互联网的系统来说是必不可少的组件,却属于没有太高存在感,通常都不会受重点关注的设施,不过 DNS 本身的工作过程,以及它对系统流量能够施加的影响,却还是有许多程序员不太了解;而且 DNS 本身就堪称是示范性的透明多级分流系统,非常符合本章的主题,值得我们去借鉴。 12 | 13 | 无论是使用浏览器抑或是在程序代码中访问某个网址域名,譬如以`www.icyfenix.com.cn`为例,如果没有缓存的话,都会先经过 DNS 服务器的解析翻译,找到域名对应的 IP 地址才能开始通信,这项操作是操作系统自动完成的,一般不需要用户程序的介入。不过,DNS 服务器并不是一次性地将“`www.icyfenix.com.cn`”直接解析成 IP 地址,需要经历一个递归的过程。首先 DNS 会将域名还原为“`www.icyfenix.com.cn.`”,注意最后多了一个点“`.`”,它是“`.root`”的含义。早期的域名必须带有这个点才能被 DNS 正确解析,如今几乎所有的操作系统、DNS 服务器都可以自动补上结尾的点号,然后开始如下解析步骤: 14 | 15 | 1. 客户端先检查本地的 DNS 缓存,查看是否存在并且是存活着的该域名的地址记录。DNS 是以[存活时间](https://en.wikipedia.org/wiki/Time_to_live)(Time to Live,TTL)来衡量缓存的有效情况的,所以,如果某个域名改变了 IP 地址,DNS 服务器并没有任何机制去通知缓存了该地址的机器去更新或者失效掉缓存,只能依靠 TTL 超期后的重新获取来保证一致性。后续每一级 DNS 查询的过程都会有类似的缓存查询操作,再遇到时笔者就不重复叙述了。 16 | 2. 客户端将地址发送给本机操作系统中配置的本地 DNS(Local DNS),这个本地 DNS 服务器可以由用户手工设置,也可以在 DHCP 分配时或者在拨号时从 PPP 服务器中自动获取到。 17 | 3. 本地 DNS 收到查询请求后,会按照“是否有`www.icyfenix.com.cn`的权威服务器”→“是否有`icyfenix.com.cn`的权威服务器”→“是否有`com.cn`的权威服务器”→“是否有`cn`的权威服务器”的顺序,依次查询自己的地址记录,如果都没有查询到,就会一直找到最后点号代表的根域名服务器为止。这个步骤里涉及了两个重要名词: 18 | - **权威域名服务器**(Authoritative DNS):是指负责翻译特定域名的 DNS 服务器,“权威”意味着这个域名应该翻译出怎样的结果是由它来决定的。DNS 翻译域名时无需像查电话本一样刻板地一对一翻译,根据来访机器、网络链路、服务内容等各种信息,可以玩出很多花样,权威 DNS 的灵活应用,在后面的内容分发网络、服务发现等章节都还会有所涉及。 19 | - **根域名服务器**(Root DNS)是指固定的、无需查询的[顶级域名](https://en.wikipedia.org/wiki/Top-level_domain)(Top-Level Domain)服务器,可以默认为它们已内置在操作系统代码之中。全世界一共有 13 组根域名服务器(注意并不是 13 台,每一组根域名都通过[任播](https://en.wikipedia.org/wiki/Anycast)的方式建立了一大群镜像,根据维基百科的数据,迄今已经超过 1000 台根域名服务器的镜像了)。13 这个数字是由于 DNS 主要采用 UDP 传输协议(在需要稳定性保证的时候也可以采用 TCP)来进行数据交换,未分片的 UDP 数据包在 IPv4 下最大有效值为 512 字节,最多可以存放 13 组地址记录,由此而来的限制。 20 | 4. 现在假设本地 DNS 是全新的,上面不存在任何域名的权威服务器记录,所以当 DNS 查询请求按步骤 3 的顺序一直查到根域名服务器之后,它将会得到“`cn`的权威服务器”的地址记录,然后通过“`cn`的权威服务器”,得到“`com.cn`的权威服务器”的地址记录,以此类推,最后找到能够解释`www.icyfenix.com.cn`的权威服务器地址。 21 | 5. 通过“`www.icyfenix.com.cn`的权威服务器”,查询`www.icyfenix.com.cn`的地址记录,地址记录并不一定就是指 IP 地址,在 RFC 规范中有定义的地址记录类型已经[多达数十种](https://en.wikipedia.org/wiki/List_of_DNS_record_types),譬如 IPv4 下的 IP 地址为 A 记录,IPv6 下的 AAAA 记录、主机别名 CNAME 记录,等等。 22 | 23 | 前面提到过,每种记录类型中还可以包括多条记录,以一个域名下配置多条不同的 A 记录为例,此时权威服务器可以根据自己的策略来进行选择,典型的应用是智能线路:根据访问者所处的不同地区(譬如华北、华南、东北)、不同服务商(譬如电信、联通、移动)等因素来确定返回最合适的 A 记录,将访问者路由到最合适的数据中心,达到智能加速的目的。 24 | 25 | DNS 系统多级分流的设计使得 DNS 系统能够经受住全球网络流量不间断的冲击,但也并非全无缺点。典型的问题是响应速度,当极端情况(各级服务器均无缓存)下的域名解析可能导致每个域名都必须递归多次才能查询到结果,显著影响传输的响应速度,譬如图 4-1 所示高达 310 毫秒的 DNS 查询。 26 | 27 | :::center 28 | ![](./images/dns-lag.png) 29 | 图 4-1 首次 DNS 请求耗时(图片来自网络) 30 | ::: 31 | 32 | 专门有一种被称为“[DNS 预取](https://en.wikipedia.org/wiki/Link_prefetching)”(DNS Prefetching)的前端优化手段用来避免这类问题:如果网站后续要使用来自于其他域的资源,那就在网页加载时生成一个 link 请求,促使浏览器提前对该域名进行预解释,譬如下面代码所示: 33 | 34 | ```html 35 | 36 | ``` 37 | 38 | 而另一种可能更严重的缺陷是 DNS 的分级查询意味着每一级都有可能受到中间人攻击的威胁,产生被劫持的风险。要攻陷位于递归链条顶层的(譬如根域名服务器,cn 权威服务器)服务器和链路是非常困难的,它们都有很专业的安全防护措施。但很多位于递归链底层或者来自本地运营商的 Local DNS 服务器的安全防护则相对松懈,甚至不少地区的运营商自己就会主动进行劫持,专门返回一个错的 IP,通过在这个 IP 上代理用户请求,以便给特定类型的资源(主要是 HTML)注入广告,以此牟利。 39 | 40 | 为此,最近几年出现了另一种新的 DNS 工作模式:[HTTPDNS](https://en.wikipedia.org/wiki/DNS_over_HTTPS)(也称为 DNS over HTTPS,DoH)。它将原本的 DNS 解析服务开放为一个基于 HTTPS 协议的查询服务,替代基于 UDP 传输协议的 DNS 域名解析,通过程序代替操作系统直接从权威 DNS 或者可靠的 Local DNS 获取解析数据,从而绕过传统 Local DNS。这种做法的好处是完全免去了“中间商赚差价”的环节,不再惧怕底层的域名劫持,能够有效避免 Local DNS 不可靠导致的域名生效缓慢、来源 IP 不准确、产生的智能线路切换错误等问题。 41 | -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/Circular_Buffer_Animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/Circular_Buffer_Animation.gif -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/ali-cdn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/ali-cdn.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/cloudflare.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/cloudflare.gif -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/dns-lag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/dns-lag.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/http-req.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/http-req.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/http2-con.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/http2-con.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/l2-lb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/l2-lb.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/l3-iptun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/l3-iptun.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/l3-nat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/l3-nat.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/l4l7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/l4l7.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/readwrite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/readwrite.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/search.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/tcp-conn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/tcp-conn.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/images/tmc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/diversion-system/images/tmc.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/diversion-system/scalability.md: -------------------------------------------------------------------------------- 1 | # 可扩缩设计 2 | 3 | ### AFK扩展立方体(XYZ轴扩展) 4 | 5 | ### 读写分离 6 | 7 | ### 分片 8 | 9 | ### 双主架构 10 | 11 | ### 数据库代理 12 | -------------------------------------------------------------------------------- /architect-perspective/general-architecture/scalability/README.md: -------------------------------------------------------------------------------- 1 | 可扩展设计 2 | 3 | AFK扩展立方体(XYZ轴扩展) 4 | 5 | 扩展连接、数据库、磁盘 -------------------------------------------------------------------------------- /architect-perspective/general-architecture/system-security/README.md: -------------------------------------------------------------------------------- 1 | # 架构安全性 2 | 3 | 即使只限定在“软件架构设计”这个语境下,系统安全仍然是一个很大的话题。我们谈论的计算机系统安全,不仅仅是指“防御系统被黑客攻击”这样狭隘的安全,,还至少应包括(不限于)以下这些问题的具体解决方案: 4 | 5 | - [**认证**](./authentication)(Authentication):系统如何正确分辨出操作用户的真实身份? 6 | - [**授权**](./authorization)( Authorization):系统如何控制一个用户该看到哪些数据、能操作哪些功能? 7 | - [**凭证**](./credentials)(Credential):系统如何保证它与用户之间的承诺是双方当时真实意图的体现,是准确、完整且不可抵赖的? 8 | - [**保密**](./confidentiality)(Confidentiality):系统如何保证敏感数据无法被包括系统管理员在内的内外部人员所窃取、滥用? 9 | - [**传输**](./transport-security)(Transport Security):系统如何保证通过网络传输的信息无法被第三方窃听、篡改和冒充? 10 | - [**验证**](./verification)(Verification):系统如何确保提交到每项服务中的数据是合乎规则的,不会对系统稳定性、数据一致性、正确性产生风险? 11 | 12 | 与安全相关的问题,一般不会直接创造价值,解决起来又烦琐复杂,费时费力,因此经常性被开发者有意无意地忽略掉。庆幸的是这些问题基本上也都是与具体系统、具体业务无关的通用性问题,这意味着它们往往会存在着业界通行的、已被验证过是行之有效的解决方案,乃至已经形成行业标准,不需要开发者自己从头去构思如何解决。 13 | 14 | 在本章中,笔者会围绕系统安全的标准方案,逐一探讨以上问题的处理办法,并会以 Fenix's Bookstore 作为案例实践。因篇幅所限,笔者没有在文中直接贴出代码,如有需要,可以在[Fenix's Bookstore 的 GitHub 仓库](/exploration/projects/)获取。此外,还有其他一些安全相关的内容主要是由管理、运维、审计领域为主导,尽管也需要软件架构和开发的配合参与,但不列入本章的讨论范围之内,譬如:安全审计、系统备份与恢复、信息系统安全法规与制度、计算机防病毒制度、保护私有信息规则,等等。 15 | -------------------------------------------------------------------------------- /architect-perspective/general-architecture/system-security/exploit.md: -------------------------------------------------------------------------------- 1 | # 漏洞利用 2 | 3 | ::: tip 漏洞利用(Exploit) 4 | 5 | 系统如何避免在基础设施和应用程序中出现弱点,被攻击者利用? 6 | 7 | ::: 8 | 9 | -------------------------------------------------------------------------------- /architect-perspective/general-architecture/system-security/images/Basic-Authentication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/system-security/images/Basic-Authentication.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/system-security/images/hmac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/system-security/images/hmac.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/system-security/images/jwt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/system-security/images/jwt.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/system-security/images/sshot-ca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/system-security/images/sshot-ca.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/system-security/images/webauthen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/system-security/images/webauthen.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/transaction/README.md: -------------------------------------------------------------------------------- 1 | # 事务处理 2 | 3 | 事务处理几乎在每一个信息系统中都会涉及,它存在的意义是为了保证系统中所有的数据都是符合期望的,且相互关联的数据之间不会产生矛盾,即数据状态的**一致性**(**C**onsistency)。 4 | 5 | 按照数据库的经典理论,要达成这个目标,需要三方面共同努力来保障。 6 | 7 | - **原子性**(**A**tomic):在同一项业务处理过程中,事务保证了对多个数据的修改,要么同时成功,要么同时被撤销。 8 | - **隔离性**(**I**solation):在不同的业务处理过程中,事务保证了各自业务正在读、写的数据互相独立,不会彼此影响。 9 | - **持久性**(**D**urability):事务应当保证所有成功被提交的数据修改都能够正确地被持久化,不丢失数据。 10 | 11 | 以上四种属性即事务的“ACID”特性,但笔者对这种说法其实不是太认同,因为这四种特性并不正交,A、I、D 是手段,C 是目的,前者是因,后者是果,弄到一块去完全是为了拼凑个单词缩写。 12 | 13 | 事务的概念虽然最初起源于数据库系统,但今天已经有所延伸,而不再局限于数据库本身了,所有需要保证数据一致性的应用场景,包括但不限于数据库、[事务内存](https://en.wikipedia.org/wiki/Transactional_memory)、缓存、消息队列、分布式存储,等等,都有可能会用到事务,后文里笔者会使用“数据源”来泛指所有这些场景中提供与存储数据的逻辑设备,但是上述场景所说的事务和一致性含义可能并不完全一致,说明如下。 14 | 15 | - 当一个服务只使用一个数据源时,通过 A、I、D 来获得一致性是最经典的做法,也是相对容易的。此时,多个并发事务所读写的数据能够被数据源感知是否存在冲突,并发事务的读写在时间线上的最终顺序是由数据源来确定的,这种事务间一致性被称为“内部一致性”。 16 | 17 | - 当一个服务使用到多个不同的数据源,甚至多个不同服务同时涉及多个不同的数据源时,问题就变得相对困难了许多。此时,并发执行甚至是先后执行的多个事务,在时间线上的顺序并不由任何一个数据源来决定,这种涉及多个数据源的事务间一致性被称为“外部一致性”。 18 | 19 | 外部一致性问题通常很难再使用 A、I、D 来解决,因为这样需要付出很大乃至不切实际的代价;但是外部一致性又是分布式系统中必然会遇到且必须要解决的问题,为此我们要转变观念,将一致性从“是或否”的二元属性转变为可以按不同强度分开讨论的多元属性,在确保代价可承受的前提下获得强度尽可能高的一致性保障,也正因如此,事务处理才从一个具体操作上的“编程问题”上升成一个需要全局权衡的“架构问题”。 20 | 21 | 人们在探索这些解决方案的过程中,产生了许多新的思路和概念,有一些概念看上去并不那么直观,在本章里,笔者会通过同一个场景事例讲解如何在不同的事务方案中处理来贯穿、理顺这些概念。 22 | 23 | :::quote 场景事例 24 | Fenix's Bookstore 是一个在线书店。每当一本书被成功售出时,需要确保以下三件事情被正确地处理: 25 | 26 | - 用户的账号扣减相应的商品款项。 27 | - 商品仓库中扣减库存,将商品标识为待配送状态。 28 | - 商家的账号增加相应的商品款项。 29 | 30 | ::: 31 | 32 | 接下来,笔者将逐一介绍在“单个服务使用单个数据源”、“单个服务使用多个数据源”、“多个服务使用单个数据源”以及“多个服务使用多个数据源”下,我们可以采用哪些手段来保证数据在以上场景中被正确地读写。 33 | -------------------------------------------------------------------------------- /architect-perspective/general-architecture/transaction/images/cap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/transaction/images/cap.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/transaction/images/force-steal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architect-perspective/general-architecture/transaction/images/force-steal.png -------------------------------------------------------------------------------- /architect-perspective/general-architecture/transaction/share.md: -------------------------------------------------------------------------------- 1 | # 共享事务 2 | 3 | 与全局事务里讨论的单个服务使用多个数据源正好相反,共享事务(Share Transaction)是指多个服务共用同一个数据源。这里有必要再强调一次“数据源”与“数据库”的区别:数据源是指提供数据的逻辑设备,不必与物理设备一一对应。在部署应用集群时最常采用的模式是将同一套程序部署到多个中间件服务器上,构成多个副本实例来分担流量压力。它们虽然连接了同一个数据库,但每个节点配有自己的专属的数据源,通常是中间件以 JNDI 的形式开放给程序代码使用。这种情况下,所有副本实例的数据访问都是完全独立的,并没有任何交集,每个节点使用的仍是最简单的本地事务。而本节讨论的是多个服务之间会产生业务交集的场景,举个具体例子,在 Fenix's Bookstore 的[场景事例](/architect-perspective/general-architecture/transaction/)中,假设用户账户、商家账户和商品仓库都存储于同一个数据库之中,但用户、商户和仓库每个领域都部署了独立的微服务,此时一次购书的业务操作将贯穿三个微服务,它们都要在数据库中修改数据。如果我们直接将不同数据源就视为是不同数据库,那上一节所讲的全局事务和下一节要讲的分布式事务都是可行的,不过,针对这种每个数据源连接的都是同一个物理数据库的特例,共享事务则有机会成为另一条可能提高性能、降低复杂度的途径,当然,也很有可能是一个伪需求。 4 | 5 | 一种**理论可行**的方案是直接让各个服务共享数据库连接,在同一个应用进程中的不同持久化工具(JDBC、ORM、JMS 等)间共享数据库连接并不困难,某些中间件服务器,譬如 WebSphere 会内置有“[可共享连接](https://www.ibm.com/support/knowledgecenter/zh/SSAW57_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/cdat_conshrnon.html)”功能来专门给予这方面的支持。但这种共享的前提是数据源的使用者都在同一个进程内,由于数据库连接的基础是网络连接,它是与 IP 地址和端口号绑定的,字面意义上的“不同服务节点共享数据库连接”很难做到,所以为了实现共享事务,就必须新增一个“交易服务器”的中间角色,无论是用户服务、商家服务还是仓库服务,它们都通过同一台交易服务器来与数据库打交道。如果将交易服务器的对外接口按照 JDBC 规范来实现的话,那它完全可以视为是一个独立于各个服务的远程数据库连接池,或者直接作为数据库代理来看待。此时三个服务所发出的交易请求就有可能做到交由交易服务器上的同一个数据库连接,通过本地事务的方式完成。譬如,交易服务器根据不同服务节点传来的同一个事务 ID,使用同一个数据库连接来处理跨越多个服务的交易事务,如图 3-4 所示。 6 | 7 | 8 | graph LR 9 | User("用户账户") --> Proxy("交易服务器") 10 | Business("商家账户") --> Proxy 11 | Warehouse("商品仓库") --> Proxy 12 | Proxy --> Database("数据库 ") 13 | 14 | 15 | :::center 16 | 图 3-4 使用同一个数据库处理多个交易服务 17 | ::: 18 | 19 | 之所以强调理论可行,是因为该方案是与实际生产系统中的压力方向相悖的,一个服务集群里数据库才是压力最大而又最不容易伸缩拓展的重灾区,所以现实中只有类似[ProxySQL](https://www.proxysql.com/)、[MaxScale](https://mariadb.com/kb/en/maxscale/)这样用于对多个数据库实例做负载均衡的数据库代理(其实用 ProxySQL 代理单个数据库,再启用 Connection Multiplexing,已经接近于前面所提及的交易服务器方案了),而几乎没有反过来代理一个数据库为多个应用提供事务协调的交易服务代理。这也是说它更有可能是个伪需求的原因,如果你有充足理由让多个微服务去共享数据库,就必须找到更加站得住脚的理由来向团队解释拆分微服务的目的是什么才行。 20 | 21 | 在日常开发中,上述方案还存在一类更为常见的变种形式:使用消息队列服务器来代替交易服务器。用户、商家、仓库的服务操作业务时,通过消息将所有对数据库的改动传送到消息队列服务器,通过消息的消费者来统一处理,实现由本地事务保障的持久化操作。这被称作“[单个数据库的消息驱动更新](https://www.infoworld.com/article/2077963/distributed-transactions-in-spring--with-and-without-xa.html)”(Message-Driven Update of a Single Database)。 22 | 23 | “共享事务”的提法和这里所列的两种处理方式在实际应用中并不值得提倡,鲜有采用这种方式的成功案例,能够查询到的资料几乎都发源于十余年前 Spring 的核心开发者[Dave Syer](https://spring.io/team/dsyer)撰写的文章《[Distributed Transactions in Spring, with and without XA](https://www.infoworld.com/article/2077963/distributed-transactions-in-spring--with-and-without-xa.html)》。笔者把共享事务列为本章四种事务类型之一只是为了叙述逻辑的完备,尽管拆分微服务后仍然共享数据库的情况在现实中并不少见,但笔者个人不赞同将共享事务作为一种常规的解决方案来考量。 24 | -------------------------------------------------------------------------------- /architecture/architect-framework/README.md: -------------------------------------------------------------------------------- 1 | # 架构核心框架 2 | 3 | -------------------------------------------------------------------------------- /architecture/architect-framework/j2ee-base-arch.md: -------------------------------------------------------------------------------- 1 | # 基于J2EE的单体架构 2 | 3 | -------------------------------------------------------------------------------- /architecture/architect-framework/kubernetes-base-arch.md: -------------------------------------------------------------------------------- 1 | # 基于Kubernetes的微服务架构 2 | 3 | -------------------------------------------------------------------------------- /architecture/architect-framework/serverless-arch-knative.md: -------------------------------------------------------------------------------- 1 | # 基于Knative的无服务架构 2 | 3 | -------------------------------------------------------------------------------- /architecture/architect-framework/serverless-arch-kubeless.md: -------------------------------------------------------------------------------- 1 | # 基于Kubeless的无服务架构 2 | 3 | -------------------------------------------------------------------------------- /architecture/architect-framework/servicemesh-lstio-arch.md: -------------------------------------------------------------------------------- 1 | # 基于Istio的服务网格架构 2 | 3 | -------------------------------------------------------------------------------- /architecture/architect-framework/springboot-base-arch.md: -------------------------------------------------------------------------------- 1 | # 基于Spring Boot的单体架构 2 | 3 | -------------------------------------------------------------------------------- /architecture/architect-framework/springcloud-base-arch.md: -------------------------------------------------------------------------------- 1 | # 基于Spring Cloud微服务架构 2 | 3 | -------------------------------------------------------------------------------- /architecture/architect-history/README.md: -------------------------------------------------------------------------------- 1 | # 服务架构演进史 2 | 3 | 架构并不是被发明出来的,而是持续演进的结果,本章我们暂且放下代码与技术,借讨论历史之名,来梳理软件架构发展历程中出现过的名词术语,以全局的视角,从这些概念的起源去分析它们是什么、它们取代了什么,以及它们为什么能够在竞争中取得成功,为什么变得不可或缺,又或者它们为什么会失败,在斗争中被淘汰,逐渐湮灭于历史的烟尘当中。 4 | -------------------------------------------------------------------------------- /architecture/architect-history/images/broke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architecture/architect-history/images/broke.png -------------------------------------------------------------------------------- /architecture/architect-history/images/coresystem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architecture/architect-history/images/coresystem.png -------------------------------------------------------------------------------- /architecture/architect-history/images/eventbus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architecture/architect-history/images/eventbus.png -------------------------------------------------------------------------------- /architecture/architect-history/images/layed-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architecture/architect-history/images/layed-arch.png -------------------------------------------------------------------------------- /architecture/architect-history/images/sidecar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/architecture/architect-history/images/sidecar.png -------------------------------------------------------------------------------- /architecture/architect-history/monolithic.md: -------------------------------------------------------------------------------- 1 | # 单体系统时代 2 | 3 | :::tip 单体架构(Monolithic) 4 | 5 | “单体”只是表明系统中主要的过程调用都是进程内调用,不会发生进程间通信,仅此而已。 6 | 7 | ::: 8 | 9 | 单体架构是今天绝大多数软件开发者都学习、实践过的一种软件架构,许多介绍微服务的书籍和技术资料中也常把这种架构风格的应用称作“[巨石系统](https://en.wikipedia.org/wiki/Monolithic_application)”(Monolithic Application)。“单体架构”在整个软件架构演进的历史进程里,是出现时间最早、应用范围最广、使用人数最多、统治历史最长的一种架构风格,但“单体”这个名称,却是在微服务开始流行之后才“事后追认”所形成的概念。此前,并没有多少人将“单体”视作一种架构来看待,如果你去查找软件架构的开发资料,可以轻而易举地找出大量以微服务为主题的书籍和文章,却很难找出专门教你如何开发单体系统的任何形式的材料,这一方面体现了单体架构本身的简单性,另一方面,也体现出在相当长的时间尺度里,大家都已经习惯了软件架构就应该是单体这种样子。 10 | 11 | 剖析单体架构之前,我们有必要先厘清一个概念误区,许多微服务的资料里,单体系统往往是以“反派角色”的身份登场的,譬如著名的微服务入门书《[微服务架构设计模式](https://book.douban.com/subject/33425123/)》,第一章的名字就是“逃离单体的地狱”。这些材料所讲的单体系统,其实都是有一个没有明说的隐含定语:“**大型的**单体系统”。对于小型系统——即由单台机器就足以支撑其良好运行的系统,单体不仅易于开发、易于测试、易于部署,且由于系统中各个功能、模块、方法的调用过程都是进程内调用,不会发生[进程间通信](https://zh.wikipedia.org/wiki/%E8%A1%8C%E7%A8%8B%E9%96%93%E9%80%9A%E8%A8%8A)(Inter-Process Communication,IPC。广义上讲,可以认为 RPC 属于 IPC 的一种特例,但请注意这里两个“PC”不是同个单词的缩写),因此也是运行效率最高的一种架构风格,完全不应该被贴上“反派角色”的标签,反倒是那些爱赶技术潮流却不顾需求现状的微服务吹捧者更像是个反派。单体系统的不足,必须基于软件的性能需求超过了单机,软件的开发人员规模明显超过了“[2 Pizza Team](https://wiki.mbalib.com/wiki/%E4%B8%A4%E4%B8%AA%E6%8A%AB%E8%90%A8%E5%8E%9F%E5%88%99)”范畴的前提下才有讨论的价值,因此,本文后续讨论中所说的单体,均应该是特指“大型的单体系统”,也正因如此,本节中说到“单体是出现最早的架构风格”,与上一节介绍原始分布式时代开篇提到的“使用多个独立的分布式服务共同构建一个更大型系统的设想与实际尝试,反而要比今天大家所了解的大型单体系统出现的时间更早”实际并无矛盾。 12 | 13 | :::quote Monolithic Application 14 | 15 | Monolith means composed all in one piece. The Monolithic application describes a single-tiered software application in which different components combined into a single program from a single platform. 16 | 17 | 单体意味着自包含。单体应用描述了一种由同一技术平台的不同组件构成的单层软件。 18 | 19 | :::right 20 | 21 | —— [Monolithic Application](https://en.wikipedia.org/wiki/Monolithic_application),Wikipedia 22 | 23 | ::: 24 | 25 | 尽管“Monolithic”这个词语本身的意思“巨石”确实带有一些“不可拆分”的隐含意味,但人们也不应该简单粗暴地把单体系统在维基百科上的定义“all in one piece”翻译成“铁板一块”,它其实更接近于“自给自足”(Self-Contained,计算机术语是“自包含”)的含义。不过,这种“铁板一块”的译法不能全算作段子,笔者相信肯定有一部分人说起单体架构、巨石系统的缺点时,在脑海中闪过的第一个特点就是它的“不可拆分”,难以扩展,因此才不能支撑越来越大的软件规模。这种想法看似合理,其实是有失偏颇的,至少不完整。 26 | 27 | 从纵向角度来看,笔者从未见过实际生产环境里有哪个大型的现代信息系统是完全不分层的。分层架构(Layered Architecture)已是现在几乎所有信息系统建设中都普遍认可、采用的软件设计方法,无论是单体还是微服务,抑或是其他架构风格,都会对代码进行纵向层次划分,收到的外部请求在各层之间以不同形式的数据结构进行流转传递,触及最末端的数据库后按相反的顺序回馈响应,如图 1-1 所示。对于这个意义上的“可拆分”,单体架构完全不会展露出丝毫的弱势,反而可能会因更容易开发、部署、测试而获得一些便捷性上的好处。 28 | 29 | :::center 30 | ![](./images/layed-arch.png) 31 | 图 1-1 分层架构示意
32 | 图片来自 O'Reilly 的开放文档《[Software Architecture Patterns](https://www.oreilly.com/programming/free/files/software-architecture-patterns.pdf)》 33 | ::: 34 | 35 | 从横向角度来看,单体架构也可以支持按照技术、功能、职责等维度,将软件拆分为各种模块,以便重用和管理代码。单体系统并不意味着只能有一个整体的程序封装形式,如果需要,它完全可以由多个 JAR、WAR、DLL、Assembly 或者其他模块格式来构成。即使是以横向扩展(Scale Horizontally)的角度来衡量,在负载均衡器之后同时部署若干个相同的单体系统副本,以达到分摊流量压力的效果,也是非常常见的需求。 36 | 37 | 在“拆分”这方面,单体系统的真正缺陷不在如何拆分,而在拆分之后的隔离与自治能力上的欠缺。由于所有代码都运行在同一个进程空间之内,所有模块、方法的调用都无须考虑网络分区、对象复制这些麻烦的事和性能损失。获得了进程内调用的简单、高效等好处的同时,也意味着如果任何一部分代码出现了缺陷,过度消耗了进程空间内的资源,所造成的影响也是全局性的、难以隔离的。譬如内存泄漏、线程爆炸、阻塞、死循环等问题,都将会影响整个程序,而不仅仅是影响某一个功能、模块本身的正常运作。如果消耗的是某些更高层次的公共资源,譬如端口号或者数据库连接池泄漏,影响还将会波及整台机器,甚至是集群中其他单体副本的正常工作。 38 | 39 | 同样,由于所有代码都共享着同一个进程空间,不能隔离,也就无法(其实还是有办法的,譬如使用 OSGi 这种运行时模块化框架,但是很别扭、很复杂)做到单独停止、更新、升级某一部分代码,因为不可能有“停掉半个进程,重启 1/4 个程序”这样不合逻辑的操作,所以从可维护性来说,单体系统也是不占优势的。程序升级、修改缺陷往往需要制定专门的停机更新计划,做灰度发布、A/B 测试也相对更复杂。 40 | 41 | 如果说共享同一进程获得简单、高效的代价是同时损失了各个功能模块的自治、隔离能力,那这两者孰轻孰重呢?这个问题的潜台词似乎是在比较微服务、单体架构哪种更好用、更优秀?笔者认为“好用和优秀”不会是放之四海皆准的,这点不妨举一个浅显的例子加以说明。譬如,沃尔玛将超市分为仓储部、采购部、安保部、库存管理部、巡检部、质量管理部、市场营销部等,可以划清职责,明确边界,让管理能力能支持企业的成长规模;但如果你家楼下开的小卖部,爸、妈加儿子,再算上看家的中华田园犬小黄一共也就只有四名员工,再去追求“先进管理”,划分仓储部、采购部、库存管理部……那纯粹是给自己找麻烦。单体架构下,哪怕是信息系统中两个相互毫无关联的子系统,也依然会部署在同一个进程中。当系统规模小的时候,这是优势,但当系统规模大,或程序需要修改的时候,其部署的成本、技术升级的迁移成本都会变得更为昂贵。继续以前面的例子来比喻,当公司小时,让安保部和质检部两个不相干的部门在同一栋大楼中办公是节约资源,但当公司人数增加,办公室已经拥挤不堪,最多只能在楼顶加盖新楼层(相当于增强硬件性能)来解决办公问题,而不能让安保、质检分开地方办公,这便是缺陷所在。 42 | 43 | 由于隔离能力的缺失,单体除了难以阻断错误传播、不便于动态更新程序以外,还面临难以技术异构的困难,每个模块的代码都通常需要使用一样的程序语言,乃至一样的编程框架去开发。单体系统的技术栈异构并非一定做不到,譬如 JNI 就可以让 Java 混用 C 或 C++,但这通常是迫不得已的,并不是优雅的选择。 44 | 45 | 不过,以上列举的这些问题都还不是今天以微服务取代单体系统成为潮流趋势的根本原因,笔者认为最重要的理由是:单体系统很难兼容“[Phoenix](/introduction/about-the-fenix-project.html#架构的演进)”的特性。这种架构风格潜在的观念是希望系统的每一个部件,每一处代码都尽量可靠,靠不出或少出缺陷来构建可靠系统。然而战术层面再优秀,也很难弥补战略层面的不足,单体靠高质量来保证高可靠性的思路,在小规模软件上还能运作良好,但系统规模越大,交付一个可靠的单体系统就变得越来越具有挑战性。如本书的前言开篇《[什么是"凤凰架构"](/introduction/about-the-fenix-project.html)》所说,正是随着软件架构演进,构筑可靠系统从“追求尽量不出错”,到正视“出错是必然”的观念转变,才是微服务架构得以挑战并逐步开始取代运作了数十年的单体架构的底气所在。 46 | 47 | 为了允许程序出错,为了获得隔离、自治的能力,为了可以技术异构等目标,是继为了性能与算力之后,让程序再次选择分布式的理由。然而,开发分布式程序也并不意味着一定要依靠今天的微服务架构才能实现。在新旧世纪之交,人们曾经探索过几种服务拆分方法,将一个大的单体系统拆分为若干个更小的、不运行在同一个进程的独立服务,这些服务拆分方法后来导致了[面向服务架构](https://en.wikipedia.org/wiki/Service-oriented_architecture)(Service-Oriented Architecture)的一段兴盛期,我们称其为“[SOA 时代](/architecture/architect-history/soa.html)”。 48 | -------------------------------------------------------------------------------- /architecture/architect-history/serverless.md: -------------------------------------------------------------------------------- 1 | # 无服务时代 2 | 3 | :::tip 无服务架构(Serverless) 4 | 5 | 如果说微服务架构是分布式系统这条路的极致,那无服务架构,也许就是“不分布式”的云端系统这条路的起点。 6 | 7 | ::: 8 | 9 | 人们研究分布式架构,最初是由于单台机器的性能无法满足系统的运行需要,尽管在后来架构演进过程中,容错能力、技术异构、职责划分等各方面因素都成为架构需要考虑的问题,但其中获得更好性能的需求在架构设计中依然占很大的比重。对软件研发而言,不去做分布式无疑才是最简单的,如果单台服务器的性能可以是无限的,那架构演进的结果肯定会与今天有很大的差别,分布式也好,容器化也好,微服务也好,恐怕都未必会如期出现,最起码不必一定是像今天这个样子。 10 | 11 | 绝对意义上的无限性能必然是不存在的,但在云计算落地已有十年时间的今日,相对意义的无限性能已经成为了现实。在工业界,2012 年,[Iron.io 公司](https://www.iron.io/)率先提出了“无服务”(Serverless,应该翻译为“无服务器”才合适,但现在称“无服务”已形成习惯了)的概念,2014 年开始,亚马逊发布了名为 Lambda 的商业化无服务应用,并在后续的几年里逐步得到开发者认可,发展成目前世界上最大的无服务的运行平台;到了 2018 年,中国的阿里云、腾讯云等厂商也开始跟进,发布了旗下的无服务的产品,“无服务”已成了近期技术圈里的“新网红”之一。 12 | 13 | 在学术界,2009 年,云计算概念刚提出的早期,UC Berkeley 大学曾发表的论文《[Above the Clouds: A Berkeley View of Cloud Computing](https://www2.eecs.berkeley.edu/Pubs/TechRpts/2009/EECS-2009-28.pdf)》,文中预言的云计算的价值、演进和普及在过去的十年里一一得到验证。十年之后的 2019 年,UC Berkeley 的第二篇有着相同命名风格的论文《[Cloud Programming Simplified: A Berkeley View on Serverless Computing](https://arxiv.org/abs/1902.03383)》发表,再次预言未来“无服务将会发展成为未来云计算的主要形式”,由此来看,“无服务”也同样是被主流学术界所认可的发展方向之一。 14 | 15 | > We predict that serverless computing will grow to dominate the future of cloud computing 16 | > 17 | > 我们预测无服务将会发展成为未来云计算的主要形式 18 | > 19 | > :::right 20 | > 21 | > —— [Cloud Programming Simplified: A Berkeley View on Serverless Computing](https://arxiv.org/abs/1902.03383), 2019 22 | > 23 | > ::: 24 | 25 | 无服务现在还没有一个特别权威的“官方”定义,但它的概念并没有前面各种架构那么复杂,本来无服务也是以“简单”为主要卖点的,它只涉及两块内容:后端设施(Backend)和函数(Function)。 26 | 27 | - **后端设施**是指数据库、消息队列、日志、存储,等等这一类用于支撑业务逻辑运行,但本身无业务含义的技术组件,这些后端设施都运行在云中,无服务中称其为“后端即服务”(Backend as a Service,BaaS)。 28 | - **函数**是指业务逻辑代码,这里函数的概念与粒度,都已经很接近于程序编码角度的函数了,其区别是无服务中的函数运行在云端,不必考虑算力问题,不必考虑容量规划(从技术角度可以不考虑,从计费的角度你的钱包够不够用还是要掂量一下的),无服务中称其为“函数即服务”(Function as a Service,FaaS)。 29 | 30 | 无服务的愿景是让开发者只需要纯粹地关注业务,不需要考虑技术组件,后端的技术组件是现成的,可以直接取用,没有采购、版权和选型的烦恼;不需要考虑如何部署,部署过程完全是托管到云端的,工作由云端自动完成;不需要考虑算力,有整个数据中心支撑,算力可以认为是无限的;也不需要操心运维,维护系统持续平稳运行是云计算服务商的责任而不再是开发者的责任。在 UC Berkeley 的论文中,把无服务架构下开发者不再关心这些技术层面的细节,类比成当年软件开发从汇编语言踏进高级语言的发展过程,开发者可以不去关注寄存器、信号、中断等与机器底层相关的细节,从而令生产力得到极大地解放。 31 | 32 | 无服务架构的远期前景看起来是很美好的,但笔者自己对无服务中短期内的发展并没有那么乐观。与单体架构、微服务架构不同,无服务架构有一些天生的特点决定了它现在不是,以后如果没有重大变革的话,估计也很难成为一种普适性的架构模式。无服务架构对一些适合的应用确实能够降低开发和运维环节的成本,譬如不需要交互的离线大规模计算,又譬如多数 Web 资讯类网站、小程序、公共 API 服务、移动应用服务端等都契合于无服务架构所擅长的短链接、无状态、适合事件驱动的交互形式;但另一方面,对于那些信息管理系统、网络游戏等应用,又或者说所有具有业务逻辑复杂,依赖服务端状态,响应速度要求较高,需要长连接等这些特征的应用,至少目前是相对并不适合的。这是因为无服务天生“无限算力”的假设决定了它必须要按使用量(函数运算的时间和占用的内存)计费以控制消耗算力的规模,因而函数不会一直以活动状态常驻服务器,请求到了才会开始运行,这导致了函数不便依赖服务端状态,也导致了函数会有冷启动时间,响应的性能不可能太好(目前无服务的冷启动过程大概是在数十到百毫秒级别,对于 Java 这类启动性能差的应用,甚至能到接近秒的级别)。 33 | 34 | 无论如何,云计算毕竟是大势所趋,今天信息系统建设的概念和观念,在(较长尺度的)明天都是会转变成适应云端的,笔者并不怀疑 Serverless+API 的设计方式会成为以后其中一种主流的软件形式,届时无服务还会有更广阔的应用空间。 35 | 36 | 如果说微服务架构是分布式系统这条路当前所能做到的极致,那无服务架构,也许就是“不分布式”的云端系统这条路的起点。虽然在顺序上笔者将“无服务”安排到了“微服务”和“云原生”时代之后,但它们两者并没有继承替代关系,强调这点是为了避免有读者从两者的名称与安排的顺序中产生“无服务就会比微服务更加先进”的错误想法。笔者相信软件开发的未来不会只存在某一种“最先进的”架构风格,多种具针对性的架构风格同时并存,是软件产业更有生命力的形态。笔者同样相信软件开发的未来,多种架构风格将会融合互补,“分布式”与“不分布式”的边界将逐渐模糊,两条路线在云端的数据中心中交汇。今天已经能初步看见一些使用无服务的云函数去实现微服务架构的苗头了,将无服务作为技术层面的架构,将微服务视为应用层面的架构,把它们组合起来使用是完全合理可行的。以后,无论是通过物理机、虚拟机、容器,抑或是无服务云函数,都会是微服务实现方案的候选项之一。 37 | 38 | 本节是架构演进历史的最后一节,如本章引言所说,我们谈历史,重点不在考古,而是借历史之名,理解好每种架构出现的意义与淘汰的原因,为的是更好地解决今天的现实问题,寻找出未来架构演进的发展道路。 39 | 40 | 对于架构演进的未来发展,2014 年,Martin Fowler 与 James Lewis 在《[Microservices](https://martinfowler.com/articles/microservices.html)》的结束语中曾写到,他们对于微服务日后能否被大范围地推广,最多只能持有谨慎的乐观,在无服务方兴未艾的今天,与那时微服务的情况十分相近,笔者对无服务日后的推广同样持谨慎乐观的态度。软件开发的最大挑战就在于只能在不完备的信息下决定当前要处理的问题。时至今日,依然很难预想在架构演进之路的前方,微服务和无服务之后还会出现何种形式的架构风格,但这也契合了图灵的那句名言:尽管目光所及之处,只是不远的前方,即使如此,依然可以看到那里有许多值得去完成的工作在等待我们。 41 | 42 | > We can only see a short distance ahead, but we can see plenty there that needs to be done. 43 | > 44 | > 尽管目光所及之处,只是不远的前方,即使如此,依然可以看到那里有许多值得去完成的工作在等待我们。 45 | > 46 | > :::right 47 | > 48 | > —— [Alan Turing](https://en.wikipedia.org/wiki/Alan_Turing),[Computing Machinery and Intelligence](https://en.wikipedia.org/wiki/Computing_Machinery_and_Intelligence),1950 49 | > 50 | > ::: 51 | -------------------------------------------------------------------------------- /architecture/architect-history/soa.md: -------------------------------------------------------------------------------- 1 | # SOA 时代 2 | 3 | ::: tip SOA 架构(Service-Oriented Architecture) 4 | 5 | 面向服务的架构是一次具体地、系统性地成功解决分布式服务主要问题的架构模式。 6 | 7 | ::: 8 | 9 | 为了对大型的单体系统进行拆分,让每一个子系统都能独立地部署、运行、更新,开发者们曾经尝试过多种方案,这里列举以下三种较有代表性的架构模式,具体如下。 10 | 11 | - [烟囱式架构](https://en.wikipedia.org/wiki/Information_silo)(Information Silo Architecture):信息烟囱又名信息孤岛(Information Island),使用这种架构的系统也被称为孤岛式信息系统或者烟囱式信息系统。它指的是一种完全不与其他相关信息系统进行互操作或者协调工作的设计模式。这样的系统其实并没有什么“架构设计”可言。接着上一节中企业与部门的例子来说,如果两个部门真的完全不会发生任何交互,就并没有什么理由强迫它们必须在一栋楼里办公;两个不发生交互的信息系统,让它们使用独立的数据库和服务器即可实现拆分,而唯一的问题,也是致命的问题是,企业中真的存在完全不发生交互的部门吗?对于两个信息系统来说,哪怕真的毫无业务往来关系,但系统的人员、组织、权限等主数据,会是完全独立、没有任何重叠的吗?这样“独立拆分”“老死不相往来”的系统,显然不可能是企业所希望见到的。 12 | - [微内核架构](https://en.wikipedia.org/wiki/Microkernel)(Microkernel Architecture):微内核架构也被称为插件式架构(Plug-in Architecture)。既然在烟囱式架构中,没有业务往来关系的系统也可能需要共享人员、组织、权限等一些的公共的主数据,那不妨就将这些主数据,连同其他可能被各子系统使用到的公共服务、数据、资源集中到一块,成为一个被所有业务系统共同依赖的核心(Kernel,也称为 Core System),具体的业务系统以插件模块(Plug-in Modules)的形式存在,这样也可提供可扩展的、灵活的、天然隔离的功能特性,即微内核架构,如图 1-2 所示。
这种模式很适合桌面应用程序,也经常在 Web 应用程序中使用。任何计算机系统都是由各种软件互相配合工作来实现具体功能的,本节列举的不同架构实现的软件,都可视作整个系统的某种插件。对于平台型应用来说,如果我们希望将新特性或者新功能及时加入系统,微内核架构会是一种不错的方案。微内核架构也可以嵌入到其他的架构模式之中,通过插件的方式来提供新功能的定制开发能力,如果你准备实现一个能够支持二次开发的软件系统,微内核也会是一种良好的选择。
不过,微内核架构也有它的局限和使用前提,它假设系统中各个插件模块之间是互不认识,不可预知系统将安装哪些模块,因此这些插件可以访问内核中一些公共的资源,但不会直接交互。可是,无论是企业信息系统还是互联网应用,这一前提假设在许多场景中都并不成立,我们必须找到办法,既能拆分出独立的系统,也能让拆分后的子系统之间顺畅地互相调用通信。 13 | 14 | :::center 15 | ![](./images/coresystem.png) 16 | 图 1-2 微内核架构示意
17 | 图片来自 O'Reilly 的开放文档《[Software Architecture Patterns](https://www.oreilly.com/content/software-architecture-patterns/)》 18 | ::: 19 | 20 | - [事件驱动架构](https://en.wikipedia.org/wiki/Event-driven_architecture)(Event-Driven Architecture):为了能让子系统互相通信,一种可行的方案是在子系统之间建立一套事件队列管道(Event Queues),来自系统外部的消息将以事件的形式发送至管道中,各个子系统从管道里获取自己感兴趣、能够处理的事件消息,也可以为事件新增或者修改其中的附加信息,甚至可以自己发布一些新的事件到管道队列中去,如此,每一个消息的处理者都是独立的,高度解耦的,但又能与其他处理者(如果存在该消息处理者的话)通过事件管道进行互动,如图 1-3 所示。 21 | 22 | :::center 23 | ![](./images/eventbus.png) 24 | 图 1-3 事件驱动架构示意
25 | 图片来自 O'Reilly 的开放文档《[Software Architecture Patterns](https://www.oreilly.com/content/software-architecture-patterns/)》 26 | ::: 27 | 28 | 当系统演化至事件驱动架构时,[原始分布式时代](/architecture/architect-history/primitive-distribution.html)结尾中提到的第二条通往更大规模软件的路径,即仍在并行发展的远程服务调用也迎来了 SOAP 协议的诞生(详见[远程服务调用](/architect-perspective/general-architecture/api-style/rpc.html)一文),此时“面向服务的架构”(Service Oriented Architecture,SOA)已经有了它登上软件架构舞台所需要的全部前置条件。 29 | 30 | SOA 的概念最早由 Gartner 公司在 1994 年提出,当时的 SOA 还不具备发展的条件,直至 2006 年情况才有所变化,由 IBM、Oracle、SAP 等公司共同成立了 OSOA 联盟(Open Service Oriented Architecture),用于联合制定和推进 SOA 相关行业标准。2007 年,在[结构化资讯标准促进组织]()(Organization for the Advancement of Structured Information Standards,OASIS)的倡议与支持下,OSOA 由一个软件厂商组成的松散联盟,转变为一个制定行业标准的国际组织,联合 OASIS 共同新成立了的[Open CSA](http://www.oasis-opencsa.org/)组织(Open Composite Services Architecture),这便是 SOA 的官方管理机构。 31 | 32 | 软件架构来到 SOA 时代,许多概念、思想都已经能在今天微服务中找到对应的身影了,譬如服务之间的松散耦合、注册、发现、治理,隔离、编排,等等。这些在今天微服务中耳熟能详的名词概念,大多数也是在分布式服务刚被提出时就已经可以预见的困难点。SOA 针对这些问题,甚至是针对“软件开发”这件事情本身,都进行了更加系统性、更加具体的探索。 33 | 34 | - “更具体”体现在尽管 SOA 本身还是属抽象概念,而不是特指某一种具体的技术,但它比单体架构和前面所列举的三种架构模式的操作性要更强,已经不能简单视其为一种架构风格,而是可以称为一套软件设计的基础平台了。它拥有领导制定技术标准的组织 Open CSA;有清晰软件设计的指导原则,譬如服务的封装性、自治、松耦合、可重用、可组合、无状态,等等;明确了采用 SOAP 作为远程调用的协议,依靠 SOAP 协议族(WSDL、UDDI 和一大票 WS-\*协议)来完成服务的发布、发现和治理;利用一个被称为[企业服务总线](https://zh.wikipedia.org/zh-hans/%E4%BC%81%E4%B8%9A%E6%9C%8D%E5%8A%A1%E6%80%BB%E7%BA%BF)(Enterprise Service Bus,ESB)的消息管道来实现各个子系统之间的通信交互,令各服务间在 ESB 调度下无须相互依赖却能相互通信,既带来了服务松耦合的好处,也为以后可以进一步实施[业务流程编排](https://zh.wikipedia.org/wiki/%E4%B8%9A%E5%8A%A1%E6%B5%81%E7%A8%8B%E7%AE%A1%E7%90%86)(Business Process Management,BPM)提供了基础;使用[服务数据对象](https://zh.wikipedia.org/wiki/%E6%9C%8D%E5%8A%A1%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1)(Service Data Object,SDO)来访问和表示数据,使用[服务组件架构](https://zh.wikipedia.org/wiki/%E6%9C%8D%E5%8A%A1%E7%BB%84%E4%BB%B6%E6%9E%B6%E6%9E%84)(Service Component Architecture,SCA)来定义服务封装的形式和服务运行的容器,等等。在这一整套成体系可以互相精密协作的技术组件支持下,若仅从技术可行性这一个角度来评判的话,SOA 可以算是成功地解决了分布式环境下出现的主要技术问题。 35 | 36 | - “更系统”指的是 SOA 的宏大理想,它的终极目标是希望总结出一套自上而下的软件研发方法论,希望做到企业只需要跟着 SOA 的思路,就能够一揽子解决掉软件开发过程中的全部问题,譬如该如何挖掘需求、如何将需求分解为业务能力、如何编排已有服务、如何开发测试部署新的功能,等等。这里面技术问题确实是重点和难点,但也仅仅是其中的一个方面,SOA 不仅关注技术,还关注研发过程中涉及到的需求、管理、流程和组织。如果这个目标真的能够达成,软件开发就有可能从此迈进工业化大生产的阶段,试想如果有一天写出符合客户需求的软件会像写八股文一样有迹可循、有法可依,那对软件开发者来说也许是无趣的,但整个社会实施信息化的效率肯定会有大幅的提升。 37 | 38 | SOA 在 21 世纪最初的十年里曾经盛行一时,有 IBM 等一众行业巨头厂商为其呐喊冲锋,吸引了不少软件开发商、尤其是企业级软件的开发商的跟随,最终却还是偃旗息鼓,沉寂了下去。在稍后的[远程服务调用](/architect-perspective/general-architecture/api-style/rpc.html)一节,笔者会提到 SOAP 协议被逐渐边缘化的本质原因:过于严格的规范定义带来过度的复杂性。而构建在 SOAP 基础之上的 ESB、BPM、SCA、SDO 等诸多上层建筑,进一步加剧了这种复杂性。开发信息系统毕竟不是作八股文章,过于精密的流程和理论也需要懂得复杂概念的专业人员才能够驾驭。SOA 诞生的那一天起,就已经注定了它只能是少数系统阳春白雪式的精致奢侈品,它可以实现多个异构大型系统之间的复杂集成交互,却很难作为一种具有广泛普适性的软件架构风格来推广。SOA 最终没有获得成功的致命伤与当年的[EJB](https://zh.wikipedia.org/wiki/EJB)如出一辙,尽管有 Sun Microsystems 和 IBM 等一众巨头在背后力挺,EJB 仍然败于以 Spring、Hibernate 为代表的“草根框架”,可见一旦脱离人民群众,终究会淹没在群众的海洋之中,连信息技术也不曾例外过。 39 | 40 | 读到这里,你不妨回想下“如何使用多个独立的分布式服务共同构建一个更大型系统”这个问题,再回想下“原始分布式时代”一节中 Unix DCE 提出的分布式服务的设计主旨:“让开发人员不必关心服务是远程还是本地,都能够透明地调用服务或者访问资源”。经过了三十年的技术进步,信息系统经历了巨石、烟囱、插件、事件、SOA 等的架构模式,应用受架构复杂度的牵绊却是越来越大,已经距离“透明”二字越来越远了,这是否算不自觉间忘记掉了当年的初心?接下来我们所谈论的微服务时代,似乎正是带着这样的自省式的问句而开启的。 41 | -------------------------------------------------------------------------------- /board.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageClass: board-page-class 3 | permalink: /board 4 | --- 5 | -------------------------------------------------------------------------------- /distribution/connect/README.md: -------------------------------------------------------------------------------- 1 | # 从类库到服务 2 | 3 | :::tip 通过服务来实现组件 4 | 5 | Microservice architectures will use libraries, but their primary way of componentizing their own software is by breaking down into services. 6 | 7 | 微服务架构也会使用到类库,但构成软件系统组件的主要方式是将其拆分为一个个服务。 8 | 9 | :::right 10 | 11 | —— [Martin Fowler](https://martinfowler.com/) / [James Lewis](https://twitter.com/boicy), [Microservices](https://martinfowler.com/articles/microservices.html), 2014 12 | 13 | ::: 14 | 15 | 微服务架构其中一个重要设计原则是“通过服务来实现独立自治的组件”(Componentization via Services),强调应采用“服务”(Service)而不再是“类库”(Library)来构建组件化的程序,这两者的差别在于类库是在编译期静态链接到程序中的,通过调用本地方法来使用其中的功能,而服务是进程外组件,通过调用远程方法来使用其中的功能。 16 | 17 | 采用服务来构建程序,获得的收益是软件系统“整体”与“部分”在物理层面的真正隔离,这对构筑可靠的大型软件系统来说无比珍贵,但另一面,其付出的代价也同样无可忽视,微服务架构在复杂性与执行性能方面做出了极大的让步。一套由多个微服务相互调用才能正常运作的分布式系统中,每个节点都互相扮演着服务的生产者与消费者的多重角色,形成了一套复杂的网状调用关系,此时,至少有(但不限于)以下三个问题是必须考虑并得到妥善解决的: 18 | 19 | - 对消费者来说,外部的服务由谁提供?具体在什么网络位置? 20 | - 对生产者来说,内部哪些服务需要暴露?哪些应当隐藏?应当以何种形式暴露服务?以什么规则在集群中分配请求? 21 | - 对调用过程来说,如何保证每个远程服务都接收到相对平均的流量,获得尽可能高的服务质量与可靠性? 22 | 23 | 这三个问题的解决方案,在微服务架构中通常被称为“服务发现”、“服务的网关路由”和“服务的负载均衡”。 24 | -------------------------------------------------------------------------------- /distribution/connect/composer.md: -------------------------------------------------------------------------------- 1 | # 服务编排 -------------------------------------------------------------------------------- /distribution/connect/configuration.md: -------------------------------------------------------------------------------- 1 | # 中心化配置 2 | 3 | -------------------------------------------------------------------------------- /distribution/connect/images/client-side-lb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/connect/images/client-side-lb.png -------------------------------------------------------------------------------- /distribution/connect/images/gateway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/connect/images/gateway.png -------------------------------------------------------------------------------- /distribution/connect/images/proxy-lb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/connect/images/proxy-lb.png -------------------------------------------------------------------------------- /distribution/connect/images/sd1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/connect/images/sd1.png -------------------------------------------------------------------------------- /distribution/connect/images/sd2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/connect/images/sd2.png -------------------------------------------------------------------------------- /distribution/consensus/README.md: -------------------------------------------------------------------------------- 1 | # 分布式共识算法 2 | 3 | :::tip 前置知识 4 | 5 | 关于分布式中 CAP 问题,请先阅读“[分布式事务](/architect-perspective/general-architecture/transaction/distributed.html)”中的介绍,后文中提及的一致性、可用性、网络分区等概念,均在此文中有过介绍。 6 | 7 | ::: 8 | 9 | 正式开始探讨分布式环境中面临的各种技术问题和解决方案以前,我们先把目光从工业界转到学术界,学习两三种具有代表性的分布式共识算法,为后续分布式环境中操作共享数据准备好理论基础。下面笔者从一个最浅显的场景开始,引出本章的主题: 10 | 11 | > 如果你有一份很重要的数据,要确保它长期存储在电脑上不会丢失,你会怎么做? 12 | 13 | 这不是什么脑筋急转弯的古怪问题,答案就是去买几块硬盘,把数据在不同硬盘上多备份几个副本。假设一块硬盘每年损坏的概率是 5%,那把文件复制到另一块备份盘上,由于两块硬盘同时损坏而丢失数据的概率就只有 0.25%,如果使用三块硬盘存储则是 0.0125%,四块是 0.000625%,换而言之,这已经保证了数据在一年内有超过 99.999%的概率是安全可靠的。 14 | 15 | 在软件系统里,要保障系统的**可靠性**,采用的办法与那几个备份硬盘并没有什么区别。单个节点的系统宕机无法访问数据的原因可能有很多,譬如程序出错、硬件损坏、网络分区、电源故障,等等,一年中出现系统宕机的概率也许还要高于 5%,这决定了软件系统也必须有多台机器能够拥有一致的数据副本,才有可能对外提供可靠的服务。 16 | 17 | 在软件系统里,要保障系统的**可用性**,面临的困难与硬盘备份却又有着本质的区别。硬盘之间是孤立的,不需要互相通信,备份数据是静态的,初始化后状态就不会发生改变,由人工进行的文件复制操作,很容易就保障了数据在各个备份盘中是一致的;然而分布式系统里面,我们必须考虑动态的数据如何在不可靠的网络通信条件下,依然能在各个节点之间正确复制的问题。将我们要讨论的场景作如下修改: 18 | 19 | > 如果你有一份会随时变动的数据,要确保它正确地存储于网络中的几台不同机器之上,你会怎么做? 20 | 21 | 相信最容易想到的答案一定是“数据同步”:每当数据有变化,把变化情况在各个节点间的复制视作一种事务性的操作,只有系统里每一台机器都反馈成功地完成硬盘写入后,数据的变化才宣告成功,笔者曾经在“[全局事务](/architect-perspective/general-architecture/transaction/global.html)”中介绍过,使用 2PC/3PC 就可以实现这种同步操作。同步的其中一种真实应用场景是数据库的主从全同步复制(Fully Synchronous Replication),譬如 MySQL Cluster,进行全同步复制时,会等待所有 Slave 节点的 Binlog 都完成写入后,Master 节点的事务才进行提交(这个场景中 Binlog 本身就是要同步的状态数据,不应将它看作是指令日志的集合)。然而这里有一个显而易见的缺陷,尽管可以确保 Master 节点和 Slave 节点中的数据是绝对一致的,但任何一个 Slave 节点因为任何原因未响应均会阻塞整个事务,每增加一个 Slave 节点,都导致造成整个系统可用性风险增加一分。 22 | 23 | 以同步为代表的数据复制方法,被称为**状态转移**(State Transfer),这类方法是较符合人类思维的可靠性保障手段,但通常要以牺牲可用性为代价。我们在建设分布式系统的时候,往往不能承受这样的代价,一些关键系统,必须保障数据正确可靠的前提下,对可用性的要求也非常苛刻,譬如系统要保证数据要达到 99.999999%可靠,同时系统也要达到 99.999%可用的程度,这就引出了我们的第三个问题: 24 | 25 | > 如果你有一份会随时变动的数据,要确保它正确地存储于网络中的几台不同机器之上,并且要尽可能保证数据是随时可用的,你会怎么做? 26 | 27 | 可靠性与可用性的矛盾造成了增加机器数量反而带来可用性的降低,为缓解这个矛盾,在分布式系统里主流的数据复制方法是以**操作转移**(Operation Transfer)为基础的。我们想要改变数据的状态,除了直接将目标状态赋予它之外,还有另一种常用的方法是通过某种操作,令源状态转换为目标状态。能够使用确定的操作,促使状态间产生确定的转移结果的计算模型,在计算机科学中被称为**状态机**(State Machine)。 28 | 29 | :::quote 额外知识:状态机复制 30 | 31 | [状态机](https://en.wikipedia.org/wiki/Finite-state_machine)有一个特性:任何初始状态一样的状态机,如果执行的命令序列一样,则最终达到的状态也一样。如果将此特性应用在多参与者进行协商共识上,可以理解为系统中存在多个具有完全相同的状态机(参与者),这些状态机能最终保持一致的关键就是起始状态完全一致和执行命令序列完全一致。 32 | 33 | ::: 34 | 35 | 根据状态机的特性,要让多台机器的最终状态一致,只要确保它们的初始状态是一致的,并且接收到的操作指令序列也是一致的即可,无论这个操作指令是新增、修改、删除抑或是其他任何可能的程序行为,都可以理解为要将一连串的操作日志正确地广播给各个分布式节点。广播指令与指令执行期间,允许系统内部状态存在不一致的情况,即并不要求所有节点的每一条指令都是同时开始、同步完成的,只要求在此期间的内部状态不能被外部观察到,且当操作指令序列执行完毕时,所有节点的最终的状态是一致的,这种模型就被称为**状态机复制**(State Machine Replication)。 36 | 37 | 考虑到分布式环境下网络分区现象是不可能消除的,甚至允许不再追求系统内所有节点在任何情况下的数据状态都一致,而是采用“少数服从多数”的原则,一旦系统中过半数的节点中完成了状态的转换,就认为数据的变化已经被正确地存储在系统当中,这样就可以容忍少数(通常是不超过半数)的节点失联,使得增加机器数量对系统整体的可用性变成是有益的,这种思想在分布式中被称为“[Quorum 机制]()”。 38 | 39 | 根据上述讨论,我们需要设计出一种算法,能够让分布式系统内部暂时容忍存在不同的状态,但最终能够保证大多数节点的状态达成一致;同时,能够让分布式系统在外部看来始终表现出整体一致的结果。这个让系统各节点不受局部的网络分区、机器崩溃、执行性能或者其他因素影响,都能最终表现出整体一致的过程,就被称为各个节点的**协商共识**(Consensus)。 40 | 41 | 最后,笔者还要提醒你共识(Consensus)与一致性(Consistency)的区别:一致性是指数据不同副本之间的差异,而共识是指达成一致性的方法与过程。由于翻译的关系,很多中文资料把 Consensus 同样翻译为一致性,导致网络上大量的“二手中文资料”将这两个概念混淆起来,如果你在网上看到“分布式一致性算法”,应明白其指的其实是“Distributed Consensus Algorithm”。 42 | -------------------------------------------------------------------------------- /distribution/consensus/gossip.md: -------------------------------------------------------------------------------- 1 | # Gossip 协议 2 | 3 | :::tip Gossip 4 | 5 | Trying to squash a rumor is like trying to unring a bell. 6 | 7 | :::right 8 | 9 | —— [Shana Alexander](https://en.wikipedia.org/wiki/Shana_Alexander),American Journalist 10 | 11 | ::: 12 | 13 | Paxos、Raft、ZAB 等分布式算法经常会被称作是“强一致性”的分布式共识协议,其实这样的描述抠细节概念的话是很别扭的,会有语病嫌疑,但我们都明白它的意思其实是在说“尽管系统内部节点可以存在不一致的状态,但从系统外部看来,不一致的情况并不会被观察到,所以整体上看系统是强一致性的”。与它们相对的,还有另一类被冠以“最终一致性”的分布式共识协议,这表明系统中不一致的状态有可能会在一定时间内被外部直接观察到。一种典型且极为常见的最终一致的分布式系统就是[DNS 系统](/architect-perspective/general-architecture/diversion-system/dns-lookup.html),在各节点缓存的 TTL 到期之前,都有可能与真实的域名翻译结果存在不一致。在本节中,笔者将介绍在比特币网络和许多重要分布式框架中都有应用的另一种具有代表性的“最终一致性”的分布式共识协议:Gossip 协议。 14 | 15 | Gossip 最早由[施乐公司](https://en.wikipedia.org/wiki/Xerox)(Xerox,现在可能很多人不了解施乐了,或只把施乐当一家复印产品公司看待,这家公司是计算机许多关键技术的鼻祖,图形界面的发明者、以太网的发明者、激光打印机的发明者、MVC 架构的提出者、RPC 的提出者、BMP 格式的提出者……) Palo Alto 研究中心在论文《[Epidemic Algorithms for Replicated Database Maintenance](http://bitsavers.trailing-edge.com/pdf/xerox/parc/techReports/CSL-89-1_Epidemic_Algorithms_for_Replicated_Database_Maintenance.pdf)》中提出的一种用于分布式数据库在多节点间复制同步数据的算法。从论文题目中可以看出,最初它是被称作“流行病算法”(Epidemic Algorithm)的,只是不太雅观,今天 Gossip 这个名字已经用得更为普遍了,除此以外,它还有“流言算法”、“八卦算法”、“瘟疫算法”等别名,这些名字都是很形象化的描述,反应了 Gossip 的特点:要同步的信息如同流言一般传播、病毒一般扩散。 16 | 17 | 笔者按照习惯也把 Gossip 也称作是“共识协议”,但首先必须强调它所解决的问题并不是直接与 Paxos、Raft 这些共识算法等价的,只是基于 Gossip 之上可以通过某些方法去实现与 Paxos、Raft 相类似的目标而已。一个最典型的例子是比特币网络中使用到了 Gossip 协议,用它来在各个分布式节点中互相同步区块头和区块体的信息,这是整个网络能够正常交换信息的基础,但并不能称作共识;比特币使用[工作量证明](https://en.wikipedia.org/wiki/Proof_of_work)(Proof of Work,PoW)来对“这个区块由谁来记账”这一件事情在全网达成共识,这个目标才可以认为与 Paxos、Raft 的目标是一致的。 18 | 19 | 下面,我们来了解 Gossip 的具体工作过程。相比 Paxos、Raft 等算法,Gossip 的过程十分简单,它可以看作是以下两个步骤的简单循环: 20 | 21 | - 如果有某一项信息需要在整个网络中所有节点中传播,那从信息源开始,选择一个固定的传播周期(譬如 1 秒),随机选择它相连接的 k 个节点(称为 Fan-Out)来传播消息。 22 | 23 | - 每一个节点收到消息后,如果这个消息是它之前没有收到过的,将在下一个周期内,选择除了发送消息给它的那个节点外的其他相邻 k 个节点发送相同的消息,直到最终网络中所有节点都收到了消息,尽管这个过程需要一定时间,但是理论上最终网络的所有节点都会拥有相同的消息。 24 | 25 | :::center 26 | 27 | ![](./images/gossip.gif) 28 | 29 | Gossip 传播示意图([图片来源](https://managementfromscratch.wordpress.com/2016/04/01/introduction-to-gossip/)) 30 | 31 | ::: 32 | 33 | 上图是 Gossip 传播过程的示意图,根据示意图和 Gossip 的过程描述,我们很容易发现 Gossip 对网络节点的连通性和稳定性几乎没有任何要求,它一开始就将网络某些节点只能与一部分节点[部分连通](https://en.wikipedia.org/wiki/Network_topology#Partially_connected_network)(Partially Connected Network)而不是以[全连通网络](https://en.wikipedia.org/wiki/Network_topology#Fully_connected_network)(Fully Connected Network)作为前提;能够容忍网络上节点的随意地增加或者减少,随意地宕机或者重启,新增加或者重启的节点的状态最终会与其他节点同步达成一致。Gossip 把网络上所有节点都视为平等而普通的一员,没有任何中心化节点或者主节点的概念,这些特点使得 Gossip 具有极强的鲁棒性,而且非常适合在公众互联网中应用。 34 | 35 | 同时我们也很容易找到 Gossip 的缺点,消息最终是通过多个轮次的散播而到达全网的,因此它必然会存在全网各节点状态不一致的情况,而且由于是随机选取发送消息的节点,所以尽管可以在整体上测算出统计学意义上的传播速率,但对于个体消息来说,无法准确地预计到需要多长时间才能达成全网一致。另外一个缺点是消息的冗余,同样是由于随机选取发送消息的节点,也就不可避免的存在消息重复发送给同一节点的情况,增加了网络的传输的压力,也给消息节点带来额外的处理负载。 36 | 37 | 达到一致性耗费的时间与网络传播中消息冗余量这两个缺点存在一定对立,如果要改善其中一个,就会恶化另外一个,由此,Gossip 设计了两种可能的消息传播模式:反熵(Anti-Entropy)和传谣(Rumor-Mongering),这两个名字都挺文艺的。熵(Entropy)是生活中少见但科学中很常用的概念,它代表着事物的混乱程度。反熵的意思就是反混乱,以提升网络各个节点之间的相似度为目标,所以在反熵模式下,会同步节点的全部数据,以消除各节点之间的差异,目标是整个网络各节点完全的一致。但是,在节点本身就会发生变动的前提下,这个目标将使得整个网络中消息的数量非常庞大,给网络带来巨大的传输开销。而传谣模式是以传播消息为目标,仅仅发送新到达节点的数据,即只对外发送变更信息,这样消息数据量将显著缩减,网络开销也相对较小。 38 | -------------------------------------------------------------------------------- /distribution/consensus/images/abstract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/consensus/images/abstract.png -------------------------------------------------------------------------------- /distribution/consensus/images/gossip.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/consensus/images/gossip.gif -------------------------------------------------------------------------------- /distribution/consensus/images/paxos1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/consensus/images/paxos1.png -------------------------------------------------------------------------------- /distribution/consensus/images/paxos2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/consensus/images/paxos2.png -------------------------------------------------------------------------------- /distribution/consensus/images/paxos3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/consensus/images/paxos3.png -------------------------------------------------------------------------------- /distribution/consensus/images/paxos4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/consensus/images/paxos4.png -------------------------------------------------------------------------------- /distribution/consensus/raft.md: -------------------------------------------------------------------------------- 1 | # Multi Paxos 2 | 3 | 上一节的最后,笔者举例介绍了 Basic Paxos 的活锁问题,两个提案节点互不相让地争相提出自己的提案,抢占同一个值的修改权限,导致整个系统在持续性地“反复横跳”,外部看起来就像被锁住了一样。此外,笔者还讲述过一个观点,分布式共识的复杂性,主要来源于网络的不可靠与请求的可并发两大因素,活锁问题与许多 Basic Paxos 异常场景中所遭遇的麻烦,都可以看作是源于任何一个提案节点都能够完全平等地、与其他节点并发地提出提案而带来的复杂问题。为此,Lamport 专门设计(“专门设计”的意思是在 Paxos 的论文中 Lamport 随意提了几句可以这么做)了一种 Paxos 的改进版本“Multi Paxos”算法,希望能够找到一种两全其美的办法,既不破坏 Paxos 中“众节点平等”的原则,又能在提案节点中实现主次之分,限制每个节点都有不受控的提案权利,这两个目标听起来似乎是矛盾的,但现实世界中的选举就很符合这种在平等节点中挑选意见领袖的情景。 4 | 5 | Multi Paxos 对 Basic Paxos 的核心改进是增加了“选主”的过程,提案节点会通过定时轮询(心跳),确定当前网络中的所有节点里是否存在有一个主提案节点,一旦没有发现主节点存在,节点就会在心跳超时后使用 Basic Paxos 中定义的准备、批准的两轮网络交互过程,向所有其他节点广播自己希望竞选主节点的请求,希望整个分布式系统对“由我作为主节点”这件事情协商达成一致共识,如果得到了决策节点中多数派的批准,便宣告竞选成功。当选主完成之后,除非主节点失联之后发起重新竞选,否则从此往后,就只有主节点本身才能够提出提案。此时,无论哪个提案节点接收到客户端的操作请求,都会将请求转发给主节点来完成提案,而主节点提案的时候,也就无需再次经过准备过程,因为可以视作是经过选举时的那一次准备之后,后续的提案都是对相同提案 ID 的一连串的批准过程。也可以通俗理解为选主过后,就不会再有其他节点与它竞争,相当于是处于无并发的环境当中进行的有序操作,所以此时系统中要对某个值达成一致,只需要进行一次批准的交互即可,如图 6-6 所示。 6 | 7 | 8 | sequenceDiagram 9 | 用户->>主提案节点: 操作请求 10 | 主提案节点->>决策节点: 广播Accept(id, i , value)请求 11 | 决策节点-->>主提案节点: 返回Accepted(id, i , value)应答 12 | 主提案节点->>记录节点: 形成决议,供记录节点学习 13 | 14 | 15 | :::center 16 | 17 | 图 6-6 Multi Paxos 算法时序图 18 | 19 | ::: 20 | 21 | 可能有人注意到这时候的二元组(id, value)已经变成了三元组(id, i, value),这是因为需要给主节点增加一个“任期编号”,这个编号必须是严格单调递增的,以应付主节点陷入网络分区后重新恢复,但另外一部分节点仍然有多数派,且已经完成了重新选主的情况,此时必须以任期编号大的主节点为准。当节点有了选主机制的支持,在整体来看,就可以进一步简化节点角色,不去区分提案、决策和记录节点了,统统以“节点”来代替,节点只有主(Leader)和从(Follower)的区别,此时协商共识的时序图如图 6-7 所示。 22 | 23 | 24 | sequenceDiagram 25 | 用户->>+节点(1,主): 操作请求 26 | 节点(1,主)->>节点(1,主): Accept/Accepted(id, i , value) 27 | 节点(1,主)->>节点(2): Accept(id, i , value) 28 | 节点(1,主)->>节点(3): Accept(id, i , value) 29 | 节点(3)-->>节点(1,主):Accepted(id, i , value) 30 | 节点(2)-->>节点(1,主):Accepted(id, i , value) 31 | 节点(1,主)-->>-用户: 返回结果 32 | 33 | 34 | 35 | :::center 36 | 37 | 图 6-7 有选主机制的协商共识的时序图 38 | 39 | ::: 40 | 41 | 在这个理解的基础上,我们换一个角度来重新思考“分布式系统中如何对某个值达成一致”这个问题,可以把该问题划分做三个子问题来考虑,可以证明(具体证明就不列在这里了,感兴趣的读者可参考结尾给出的论文)当以下三个问题同时被解决时,即等价于达成共识: 42 | 43 | - 如何选主(Leader Election)。 44 | - 如何把数据复制到各个节点上(Entity Replication)。 45 | - 如何保证过程是安全的(Safety)。 46 | 47 | 选主问题尽管还涉及许多工程上的细节,譬如心跳、随机超时、并行竞选,等等,但要只论原理的话,如果你已经理解了 Paxos 算法的操作步骤,相信对选主并不会有什么疑惑,因为这本质上仅仅是分布式系统对“谁来当主节点”这件事情的达成的共识而已,我们在前一节已经花了数千字来讲述分布式系统该如何对一件事情达成共识,这里就不重复赘述了,下面直接来解决数据(Paxos 中的提案、Raft 中的日志)在网络各节点间的复制问题。 48 | 49 | 在正常情况下,当客户端向主节点发起一个操作请求,譬如提出“将某个值设置为 X”,此时主节点将 X 写入自己的变更日志,但先不提交,接着把变更 X 的信息在下一次心跳包中广播给所有的从节点,并要求从节点回复确认收到的消息,从节点收到信息后,将操作写入自己的变更日志,然后给主节点发送确认签收的消息,主节点收到过半数的签收消息后,提交自己的变更、应答客户端并且给从节点广播可以提交的消息,从节点收到提交消息后提交自己的变更,数据在节点间的复制宣告完成。 50 | 51 | 在异常情况下,网络出现了分区,部分节点失联,但只要仍能正常工作的节点的数量能够满足多数派(过半数)的要求,分布式系统就仍然可以正常工作,这时候数据复制过程如下: 52 | 53 | - 假设有 S1、S2、S3、S4、S5五个节点,S1是主节点,由于网络故障,导致 S1、S2和 S3、S4、S5之间彼此无法通信,形成网络分区。 54 | 55 | - 一段时间后,S3、S4、S5三个节点中的某一个(譬如是 S3)最先达到心跳超时的阈值,获知当前分区中已经不存在主节点了,它向所有节点发出自己要竞选的广播,并收到了 S4、S5节点的批准响应,加上自己一共三票,即得到了多数派的批准,竞选成功,此时系统中同时存在 S1和 S3两个主节点,但由于网络分区,它们不会知道对方的存在。 56 | - 这种情况下,客户端发起操作请求: 57 | - 如果客户端连接到了 S1、S2之一,都将由 S1处理,但由于操作只能获得最多两个节点的响应,不构成多数派的批准,所以任何变更都无法成功提交。 58 | - 如果客户端连接到了 S3、S4、S5之一,都将由 S3处理,此时操作可以获得最多三个节点的响应,构成多数派的批准,是有效的,变更可以被提交,即系统可以继续提供服务。 59 | - 事实上,以上两种“如果”情景很少机会能够并存。网络分区是由于软、硬件或者网络故障而导致的,内部网络出现了分区,但两个分区仍然能分别与外部网络的客户端正常通信的情况甚为少见。更多的场景是算法能容忍网络里下线了一部分节点,按照这个例子来说,如果下线了两个节点,系统正常工作,下线了三个节点,那剩余的两个节点也不可能继续提供服务了。 60 | - 假设现在故障恢复,分区解除,五个节点可以重新通信了: 61 | - S1和 S3都向所有节点发送心跳包,从各自的心跳中可以得知两个主节点里 S3的任期编号更大,它是最新的,此时五个节点均只承认 S3是唯一的主节点。 62 | - S1、S2回滚它们所有未被提交的变更。 63 | - S1、S2从主节点发送的心跳包中获得它们失联期间发生的所有变更,将变更提交写入本地磁盘。 64 | - 此时分布式系统各节点的状态达成最终一致。 65 | 66 | 下面我们来看第三个问题:“如何保证过程是安全的”,不知你是否感觉到这个问题与前两个存在一点差异?选主、数据复制都是很具体的行为,但是“安全”就很模糊,什么算是安全或者不安全? 67 | 68 | 在分布式理论中,`Safety`和`Liveness`两种属性是有预定义的术语,在专业的资料中一般翻译成“协定性”和“终止性”,这两个概念也是由 Lamport 最先提出,当时给出的定义是: 69 | 70 | - 协定性(Safety):所有的坏事都不会发生(something "bad" will never happen)。 71 | - 终止性(Liveness):所有的好事都终将发生,但不知道是啥时候(something "good" will must happen, but we don't know when)。 72 | 73 | 这种就算解释了你也看不明白的定义,是不是很符合 Lamport 老爷子一贯的写作风格?(笔者无奈地摊手苦笑)。我们不去纠结严谨的定义,仍通过举例来说明它们的具体含义。譬如以选主问题为例,Safety 保证了选主的结果一定是有且只有唯一的一个主节点,不可能同时出现两个主节点;而 Liveness 则要保证选主过程是一定可以在某个时刻能够结束的。由前面对活锁的介绍可以得知,在 Liveness 这个属性上选主问题是存在理论上的瑕疵的,可能会由于活锁而导致一直无法选出明确的主节点,所以 Raft 论文中只写了对 Safety 的保证,但由于工程实现上的处理,现实中是几乎不可能会出现终止性的问题。 74 | 75 | 最后,以上这种把共识问题分解为“Leader Election”、“Entity Replication”和“Safety”三个问题来思考、解决的解题思路,即“Raft 算法”,这篇以《[一种可以让人理解的共识算法](https://web.stanford.edu/~ouster/cgi-bin/papers/raft-atc14)》(In Search of an Understandable Consensus Algorithm)为题的论文提出了 Raft 算法,并获得了 USENIX ATC 2014 大会的 Best Paper,后来更是成为 Etcd、LogCabin、Consul 等重要分布式程序的实现基础,ZooKeeper 的 ZAB 算法与 Raft 的思路也非常类似,这些算法都被认为是 Multi Paxos 的等价派生实现。 76 | -------------------------------------------------------------------------------- /distribution/observability/README.md: -------------------------------------------------------------------------------- 1 | # 可观测性 2 | 3 | 随着分布式架构渐成主流,[可观测性](https://en.wikipedia.org/wiki/Observability)(Observability)一词也日益频繁地被人提起。最初,它与[可控制性](https://en.wikipedia.org/wiki/Controllability)(Controllability)一起,是由匈牙利数学家 Rudolf E. Kálmán 针对线性动态控制系统提出的一组对偶属性,原本的含义是“可以由其外部输出推断其内部状态的程度”。 4 | 5 | 在学术界,虽然“可观测性”这个名词是近几年才从控制理论中借用的舶来概念,不过其内容实际在计算机科学中已有多年的实践积累。学术界一般会将可观测性分解为三个更具体方向进行研究,分别是:[事件日志](/distribution/observability/logging.html)、[链路追踪](/distribution/observability/tracing.html)和[聚合度量](/distribution/observability/metrics.html),这三个方向各有侧重,又不是完全独立,它们天然就有重合或者可以结合之处,2017 年的分布式追踪峰会(2017 Distributed Tracing Summit)结束后,Peter Bourgon 撰写了总结文章《[Metrics, Tracing, and Logging](https://peter.bourgon.org/blog/2017/02/21/metrics-tracing-and-logging.html)》系统地阐述了这三者的定义、特征,以及它们之间的关系与差异,受到了业界的广泛认可。 6 | 7 | :::center 8 | ![](./images/mtl.png) 9 | 图 10-1 日志、追踪、度量的目标与结合([图片来源](https://peter.bourgon.org/blog/2017/02/21/metrics-tracing-and-logging.html)) 10 | ::: 11 | 12 | 假如你平时只开发单体系统,从未接触过分布式系统的观测工作,那看到日志、追踪和度量,很有可能只会对日志这一项感到熟悉,其他两项会相对陌生。然而按照 Peter Bourgon 给出的定义来看,尽管分布式系统中追踪和度量必要性和复杂程度确实比单体系统时要更高,但是在单体时代,你肯定也已经接触过以上全部三项的工作,只是并未意识到而已,笔者将它们的特征转述如下: 13 | 14 | - **日志**(Logging):日志的职责是记录离散事件,通过这些记录事后分析出程序的行为,譬如曾经调用过什么方法,曾经操作过哪些数据,等等。打印日志被认为是程序中最简单的工作之一,调试问题时常有人会说“当初这里记得打点日志就好了”,可见这就是一项举手之劳的任务。输出日志的确很容易,但收集和分析日志却可能会很复杂,面对成千上万的集群节点,面对迅速滚动的事件信息,面对数以 TB 计算的文本,传输与归集都并不简单。对大多数程序员来说,分析日志也许就是最常遇见也最有实践可行性的“大数据系统”了。 15 | 16 | - **追踪**(Tracing):单体系统时代追踪的范畴基本只局限于[栈追踪](https://en.wikipedia.org/wiki/Stack_trace)(Stack Tracing),调试程序时,在 IDE 打个断点,看到的 Call Stack 视图上的内容便是追踪;编写代码时,处理异常调用了 `Exception::printStackTrace()`方法,它输出的堆栈信息也是追踪。微服务时代,追踪就不只局限于调用栈了,一个外部请求需要内部若干服务的联动响应,这时候完整的调用轨迹将跨越多个服务,同时包括服务间的网络传输信息与各个服务内部的调用堆栈信息,因此,分布式系统中的追踪在国内常被称为“全链路追踪”(后文就直接称“链路追踪”了),许多资料中也称它为“[分布式追踪](https://opentracing.io/docs/overview/what-is-tracing/)”(Distributed Tracing)。追踪的主要目的是排查故障,如分析调用链的哪一部分、哪个方法出现错误或阻塞,输入输出是否符合预期,等等。 17 | 18 | - **度量**(Metrics):度量是指对系统中某一类信息的统计聚合。譬如,证券市场的每一只股票都会定期公布财务报表,通过财报上的营收、净利、毛利、资产、负债等等一系列数据来体现过去一个财务周期中公司的经营状况,这便是一种信息聚合。Java 天生自带有一种基本的度量,就是由虚拟机直接提供的 JMX(Java Management eXtensions)度量,诸如内存大小、各分代的用量、峰值的线程数、垃圾收集的吞吐量、频率,等等都可以从 JMX 中获得。度量的主要目的是监控(Monitoring)和预警(Alert),如某些度量指标达到风险阈值时触发事件,以便自动处理或者提醒管理员介入。 19 | 20 | 在工业界,目前针对可观测性的产品已经是一片红海,经过多年的角逐,日志、度量两个领域的胜利者算是基本尘埃落定。日志收集和分析大多被统一到 Elastic Stack(ELK)技术栈上,如果说未来还能出现什么变化的话,也就是其中的 Logstash 能看到有被 Fluentd 取代的趋势,让 ELK 变成 EFK,但整套 Elastic Stack 技术栈的地位已是相当稳固。度量方面,跟随着 Kubernetes 统一容器编排的步伐,Prometheus 也击败了度量领域里以 Zabbix 为代表的众多前辈,即将成为云原生时代度量监控的事实标准,虽然从市场角度来说 Prometheus 还没有达到 Kubernetes 那种“拔剑四顾,举世无敌”的程度,但是从社区活跃度上看,Prometheus 已占有绝对的优势,在 Google 和 CNCF 的推动下,未来前途可期。 21 | 22 | :::quote 额外知识:Kubernetes 与 Prometheus 的关系 23 | 24 | Kubernetes 是 CNCF 第一个孵化成功的项目,Prometheus 是 CNCF 第二个孵化成功的项目。
Kubernetes 起源于 Google 的编排系统 Borg,Prometheus 起源于 Google 为 Borg 做的度量监控系统 BorgMon。 25 | 26 | ::: 27 | 28 | 追踪方面的情况与日志、度量有所不同,追踪是与具体网络协议、程序语言密切相关的,收集日志不必关心这段日志是由 Java 程序输出的还是由 Golang 程序输出的,对程序来说它们就只是一段非结构化文本而已,同理,度量对程序来说也只是一个个聚合的数据指标而已。但链路追踪就不一样,各个服务之间是使用 HTTP 还是 gRPC 来进行通信会直接影响追踪的实现,各个服务是使用 Java、Golang 还是 Node.js 来编写,也会直接影响到进程内调用栈的追踪方式。这决定了追踪工具本身有较强的侵入性,通常是以插件式的探针来实现;也决定了追踪领域很难出现一家独大的情况,通常要有多种产品来针对不同的语言和网络。近年来各种链路追踪产品层出不穷,市面上主流的工具既有像 Datadog 这样的一揽子商业方案,也有 AWS X-Ray 和 Google Stackdriver Trace 这样的云计算厂商产品,还有像 SkyWalking、Zipkin、Jaeger 这样来自开源社区的优秀产品。 29 | 30 | :::center 31 | ![](./images/cncf.png) 32 | 图 10-2 日志、追踪、度量的相关产品([图片来源](https://landscape.cncf.io/)) 33 | ::: 34 | 35 | 图 10-2 是[CNCF Interactive Landscape](https://landscape.cncf.io/)中列出的日志、追踪、度量领域的著名产品,其实这里很多不同领域的产品是跨界的,譬如 ELK 可以通过 Metricbeat 来实现度量的功能,Apache SkyWalking 的探针就有同时支持度量和追踪两方面的数据来源,由[OpenTracing](https://opentracing.io/)进化而来[OpenTelemetry](https://opentelemetry.io/)更是融合了日志、追踪、度量三者所长,有望成为三者兼备的统一可观测性解决方案。本章后面的讲解,也会扣紧每个领域中最具有统治性产品来进行介绍。 36 | 37 | 图 10-2 38 | -------------------------------------------------------------------------------- /distribution/observability/images/cncf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/observability/images/cncf.png -------------------------------------------------------------------------------- /distribution/observability/images/kibana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/observability/images/kibana.png -------------------------------------------------------------------------------- /distribution/observability/images/logging.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/observability/images/logging.jpg -------------------------------------------------------------------------------- /distribution/observability/images/mon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/observability/images/mon.png -------------------------------------------------------------------------------- /distribution/observability/images/mtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/observability/images/mtl.png -------------------------------------------------------------------------------- /distribution/observability/images/opentracing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/observability/images/opentracing.png -------------------------------------------------------------------------------- /distribution/observability/images/pinpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/observability/images/pinpoint.png -------------------------------------------------------------------------------- /distribution/observability/images/prometheus_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/observability/images/prometheus_architecture.png -------------------------------------------------------------------------------- /distribution/observability/images/spans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/observability/images/spans.png -------------------------------------------------------------------------------- /distribution/secure/README.md: -------------------------------------------------------------------------------- 1 | # 可靠通讯 2 | 3 | 微服务提倡[分散治理](/architecture/architect-history/microservices.html)(Decentralized Governance),不追求统一的技术平台,提倡让团队有自由选择的权利,不受制于语言和技术框架。在开发阶段构建服务时,分散治理打破了由技术栈带来的约束,好处是不言自明的。但在运维阶段部署服务时,尤其是考量安全问题时,由 Java、Golang、Python、Node.js 等多种语言和框架共同组成的微服务系统,出现安全漏洞的概率肯定要比只采用其中某种语言、某种框架所构建的单体系统更高。为了避免由于单个服务节点出现漏洞被攻击者突破,进而导致整个系统和内网都遭到入侵,我们就必须打破一些传统的安全观念,以构筑更加可靠的服务间通信机制。 4 | -------------------------------------------------------------------------------- /distribution/traffic-management/README.md: -------------------------------------------------------------------------------- 1 | # 流量治理 2 | 3 | :::tip 容错性设计 4 | 5 | Since services can fail at any time, it's important to be able to detect the failures quickly and, if possible, automatically restore service 6 | 7 | 由于服务随时都有可能崩溃,因此快速的失败检测和自动恢复就显得至关重要。 8 | 9 | :::right 10 | 11 | —— [Martin Fowler](https://martinfowler.com/) / [James Lewis](https://twitter.com/boicy), [Microservices](https://martinfowler.com/articles/microservices.html), 2014 12 | 13 | ::: 14 | 15 | “容错性设计”(Design for Failure)是微服务的另一个[核心原则](/architecture/architect-history/microservices.html),也是笔者书中多次反复强调的开发观念转变。不过,即使已经有一定的心理准备,大多数首次将微服务架构引入实际生产系统的开发者,在[服务发现](/distribution/connect/service-discovery.html)、[网关路由](/distribution/connect/service-routing.html)等支持下,踏出了服务化的第一步以后,很可能仍会经历一段阵痛期,随着拆分出的服务越来越多,随之而来会面临以下两个问题的困扰: 16 | 17 | - 由于某一个服务的崩溃,导致所有用到这个服务的其他服务都无法正常工作,一个点的错误经过层层传递,最终波及到调用链上与此有关的所有服务,这便是雪崩效应。如何防止雪崩效应便是微服务架构容错性设计原则的具体实践,否则服务化程度越高,整个系统反而越不稳定。 18 | - 服务虽然没有崩溃,但由于处理能力有限,面临超过预期的突发请求时,大部分请求直至超时都无法完成处理。这种现象产生的后果跟交通堵塞是类似的,如果一开始没有得到及时的治理,后面就需要长时间才能使全部服务都恢复正常。 19 | 20 | 本章我们将围绕以上两个问题,提出服务容错、流量控制等一系列解决方案。这些措施并不是孤立的,它们相互之间存在很多联系,其中许多功能必须与此前介绍过的服务注册中心、服务网关、负载均衡器配合才能实现。理清楚这些技术措施背后的逻辑链条,是了解它们工作原理的捷径。 21 | -------------------------------------------------------------------------------- /distribution/traffic-management/exception-inject.md: -------------------------------------------------------------------------------- 1 | # 异常注入 2 | 3 | -------------------------------------------------------------------------------- /distribution/traffic-management/images/hop1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/traffic-management/images/hop1.png -------------------------------------------------------------------------------- /distribution/traffic-management/images/isolation-none.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/traffic-management/images/isolation-none.png -------------------------------------------------------------------------------- /distribution/traffic-management/images/isolation-tp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/traffic-management/images/isolation-tp.png -------------------------------------------------------------------------------- /distribution/traffic-management/images/state_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/distribution/traffic-management/images/state_diagram.png -------------------------------------------------------------------------------- /distribution/traffic-management/qos.md: -------------------------------------------------------------------------------- 1 | # 服务质量 -------------------------------------------------------------------------------- /distribution/traffic-management/service-downgrade.md: -------------------------------------------------------------------------------- 1 | # 降级 2 | 3 | -------------------------------------------------------------------------------- /distribution/traffic-management/timeout.md: -------------------------------------------------------------------------------- 1 | # 超时 -------------------------------------------------------------------------------- /exploration/guide/quick-start.md: -------------------------------------------------------------------------------- 1 | # 如何开始 2 | 3 | 《凤凰架构:构建可靠的大型分布式系统》这部开源文档项目是笔者对自己在软件架构方面知识的总结,它是完全免费开放的,但免费的、开源的文档并不意味着你使用它时就没有成本,也不见得这个文档中所有的内容对每一个开发人员来说都是必要的。鲁迅说浪费别人的时间等于谋财害命,为了避免浪费阅读者的时间和精力,笔者除了自身力求在知识点准确性和叙述流畅性方面保证质量之外,同时也在本文中简要介绍每一章的主题和所面向的读者类型,本文档各章节之间并没有明显的前后依赖关系,有针对性的翻阅是完全可行的,无需一篇不漏地顺序阅读,在[此目录](/summary)中列出了各文章的明细及字数,希望有助于你制定阅读计划。 4 | 5 | ## 引导篇 探索起步 6 | 7 | **这部分面向于准备对文档介绍的内容亲身实践的探索者。** 8 | 9 | 这部分没有知识性的内容,是整部文档的引导,也可以视为这个文档附带示例工程的说明书,以及相应运行环境的部署手册。之所以将它安排在目录的第一位,是因为笔者相信如果你是一名驾驶初学者,最合理的学习路径应该是先把汽车发动,然后慢慢行驶起来,而不是马上从“引擎动力原理”、“变速箱构造”入手去设法深刻地了解一台汽车。相信计算机技术也是同理,先从运行程序,看看效果,搭建好开发、调试环境,对即将进行的工作有一个整体的认知开始是很有好处的。 10 | 11 | :::quote 工程提示 12 | 本文档所涉及到的工程均在 GitHub 上存有独立的项目,以方便构建、阅读、运行和 fork。 13 | 14 | 这部分中的部分内容,是由这些工程的 README.md 文件人工同步而来,并没有通过持续集成工具自动处理,所以可能有偶尔更新不一致的情况,如可能,建议到这些项目的 GitHub 页面上查看最新情况,在右上角有相应的超链接。如这些工程对你有用,望请不吝给个Star 。 15 | ::: 16 | 17 | 这部分所提供的工程是作为后面所述知识的演示样例,由于数量确实不少,并无必要一次性地把上面所有的工程都运行起来。因为它们是采用不同的技术来解决同一个问题,所以每个工程执行后,最终看到的界面效果均是一样的,只是实现的架构不同。而通过不同的架构、技术去解决同一个问题,这也正是这批工程的最大价值所在。 18 | 19 | 如果你本身对某些架构风格已经熟练掌握,那笔者的建议是不妨选择一种你目前关注的架构风格去运行起来,然后与你熟悉的技术方案进行比对(引导篇 探索起步)、了解它解决的问题与背景(第一部分 演进中的架构)、思考这种架构涉及到哪些标准方案(第二部分 架构师的视角)、理清分布式系统中新的挑战与应对(第三部分 分布式的基石),以及如何将这些纯粹的技术问题隐藏起来,使其不会干扰业务代码(第四部分 不可变基础设施)。 20 | 21 | ## 第一部分 演进中的架构 22 | 23 | **这部分适合所有开发者,但尤其推荐刚刚从单体架构向微服务架构转型的开发者去阅读。** 24 | 25 | 架构并不是“发明”出来的,是持续进化的结果。“服务架构演进史”这部分,笔者假借讨论历史之名,来梳理微服务发展里程中出现的大量名词、概念,借着微服务的演变过程,我们将从这些概念起源的最初,去分析它们是什么、它们取代了什么、以及它们为什么能够在斗争中取得成功,为什么变得不可或缺的支撑,又或者它们为什么会失败,在竞争中被淘汰,或逐渐湮灭于历史的烟尘当中。 26 | 27 | ## 第二部分 架构师的视角 28 | 29 | **这部分讨论与风格无关的架构知识,适合所有技术架构师、系统设计、开发人员。** 30 | 31 | “架构师”这个词的外延非常宽泛,不同语境中有不同所指,这部文档中的技术架构师特指的是[企业架构](https://wiki.mbalib.com/wiki/%E4%BC%81%E4%B8%9A%E6%9E%B6%E6%9E%84)中面向技术模型的系统设计者,这意味着讨论范围**不会**涉及到贴近于企业战略、业务流程的系统分析、信息战略设计等内容,而是聚焦于贴近一线研发人员的技术方案设计者。这部分将介绍作为一个架构师,你应该在做架构设计时思考哪些问题,有哪些主流的解决方案和行业标准做法,各种方案有什么优点、缺点,不同的解决方法会带来什么不同的影响,等等。以达到将“架构设计”这种听起来抽象的工作具体化、具象化的目的。 32 | 33 | 这部分介绍的内容与具体哪一种架构风格无关,作为后续实践的基础,讨论的是普适的架构技术与技巧,无论你是否关注微服务、云原生这些概念,无论你是从事架构设计还是从事编码开发,了解这里所列的基础知识,对每一个技术人员都是有价值的。 34 | 35 | ## 第三部分 分布式的基石 36 | 37 | **这部分面向于使用分布式架构的开发人员。** 38 | 39 | 只要选择了分布式架构,无论是 SOA、微服务、服务网格或者其他架构风格,涉及与远程服务交互时,服务的注册发现、跟踪治理、负载均衡、故障隔离、认证授权、伸缩扩展、传输通讯、事务处理,等等,这一系列问题都是无可避免的。不同的架构风格,其区别是到底要在技术规范上提供统一的解决方案,还是由应用系统自行去解决,又或者在基础设施层面将一类问题隔离掉,这部分将会讨论这类问题的解决思路、方法和常见工具。 40 | 41 | ## 第四部分 不可变基础设施 42 | 43 | **这部分面向于基础设施运维人员、技术平台的开发者。** 44 | 45 | “不可变基础设施”这个概念由来已久。2012 年 Martin Fowler 设想的“[凤凰服务器](https://martinfowler.com/bliki/PhoenixServer.html)”与 2013 年 Chad Fowler 正式提出的“[不可变基础设施](http://chadfowler.com/2013/06/23/immutable-deployments.html)”,都阐明了基础设施不变性所能带来的益处。在[云原生基金会](https://en.wikipedia.org/wiki/Cloud_Native_Computing_Foundation)(Cloud Native Computing Foundation,CNCF)所定义的“云原生”概念中,“不可变基础设施”提升到了与微服务平级的重要程度,此时它的内涵已不再局限于方便运维、程序升级和部署的手段,而是升华为向应用代码隐藏分布式架构复杂度、让分布式架构得以成为一种可普遍推广的普适架构风格的必要前提。在[云原生时代、后微服务时代](/architecture/architect-history/post-microservices.html)中,软件与硬件之间的界线已经彻底模糊,无论是基础设施的运维人员,抑或技术平台的开发人员,都有必要深入理解基础设施不变性的目的、原理与实现途径。 46 | 47 | ## 第五部分 技术方法论 48 | 49 | **这部分面向于在企业中能对重要技术决策进行拍板的决策者。** 50 | 51 | 这部文档的主体内容是务实的,多谈具体技术,少谈方向理论。只在这部分中会集中讨论几点与分布式、微服务、架构等相关的相对务虚的话题。 52 | 53 | 笔者认为对于一个技术人员,成长主要的驱动力是实践,在开发程序、解决问题中增长自身的知识,再将知识归纳、总结、升华成为理论的,所以笔者将这部分安排到了整部文档的末尾,也是希望大家能先去实践,再谈理论。同时,笔者也认为对于一名研究人员,或者企业中真正能决定技术方向的决策者,理论与实践都不可缺少,涉及决策的场景中,成体系的理论知识甚至比实践经验还要关键,因为执行力再强也必须用在正确的方向上才有价值。如果你对自己的规划是有朝一日要从一名技术人员发展成研究或者管理角色,补充这部分知识是必不可少的。 54 | 55 | ## 篇外 随笔文章 56 | 57 | **这部分无特定读者对象,内容是笔者日常文章的整理。** 58 | 59 | 这部分是一些笔者所了解的开发、设计中心得感悟的集合,由于它们还不具备足够的系统性,没有安排入前面的知识框架之中。但有一些或精彩,或有价值,或实用的技巧,笔者不想错过,所以安排了这一章相对独立的内容。 60 | 61 | 另外,这部分内容类似于笔者的随笔博客,将不会出现在文档的音频版与传统纸质书版本中。 62 | 63 | ## 篇外 附录 64 | 65 | **这部分面向刚开始接触云原生环境的设计者、开发者。** 66 | 67 | 这一章内容主要是云原生环境搭建和程序发布过程,原本它们并不属于笔者准备讨论的重点话题,至少没有到单独开一章的必要程度。但由于容器化的服务编排环境本身构建、管理和运维都有一定的复杂性,尤其是在国内特殊的网络环境下,无法直接访问到 Google 等国外的代码仓库,以至于不得不通过手工预载镜像或者代理的方式来完成环境搭建。为了避免刚刚接触这一领域的读者在入门第一步就受到不必要的心理打击,笔者专门设置了这个目录章节。这章与其他几章讨论设计思想、实现原理的风格差异很大,它是整部文档唯一的讨论具体如何操作的内容。 68 | 69 | 市面上介绍如何安装环境的书籍、资料已经不计其数,肯定有相当一部分读者这章的内容本身就是了解的,已掌握的读者建议无需仔细阅读,在有需要的时候,可当作工具查阅。 70 | -------------------------------------------------------------------------------- /exploration/projects/README.md: -------------------------------------------------------------------------------- 1 | # 技术演示工程 2 | 3 | 除文档部分外,笔者同时还建立了若干配套的代码工程,这是针对不同架构、技术方案(如单体架构、微服务、服务网格、无服务架构,等等)的演示程序。它们既是文档中所述知识的实践示例,亦可作为实际项目新创建时的可参考引用的基础代码。 4 | 5 | 本小节内容是由这些工程的 README.md 文件同步而来,由于未经过持续集成工具自动处理,所以可能有偶尔更新不一致的情况,如可能,建议到这些项目的 GitHub 页面上查看最新情况。 6 | 7 | - 文档工程: 8 | - 凤凰架构:[https://icyfenix.cn](https://icyfenix.cn) 9 | - Vuepress 支持的文档工程:[https://github.com/fenixsoft/awesome-fenix](https://github.com/fenixsoft/awesome-fenix) 10 | - 前端工程: 11 | - Mock.js 支持的纯前端演示:[https://bookstore.icyfenix.cn](https://bookstore.icyfenix.cn) 12 | - Vue.js 2 实现前端工程:[https://github.com/fenixsoft/fenix-bookstore-frontend](https://github.com/fenixsoft/fenix-bookstore-frontend) 13 | - 后端工程: 14 | - Spring Boot 实现单体架构:[https://github.com/fenixsoft/monolithic_arch_springboot](https://github.com/fenixsoft/monolithic_arch_springboot) 15 | - Spring Cloud 实现微服务架构:[https://github.com/fenixsoft/microservice_arch_springcloud](https://github.com/fenixsoft/microservice_arch_springcloud) 16 | - Kubernetes 为基础设施的微服务架构:[https://github.com/fenixsoft/microservice_arch_kubernetes](https://github.com/fenixsoft/microservice_arch_kubernetes) 17 | - Istio 为基础设施的服务网格架构:[https://github.com/fenixsoft/servicemesh_arch_istio](https://github.com/fenixsoft/servicemesh_arch_istio) 18 | - AWS Lambda 为基础的无服务架构:[https://github.com/fenixsoft/serverless_arch_awslambda](https://github.com/fenixsoft/serverless_arch_awslambda) 19 | -------------------------------------------------------------------------------- /exploration/projects/images/ddd-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/exploration/projects/images/ddd-arch.png -------------------------------------------------------------------------------- /exploration/projects/serverless_arch.md: -------------------------------------------------------------------------------- 1 | # 无服务:AWS Lambda 2 | 3 | 4 | 5 |

6 | 7 | logo 8 | 9 |

10 |

11 | 12 | License 13 | Document License 14 | About Author 15 |

16 | 17 |
18 | 19 | 如果你此时并不曾了解过什么是“The Fenix Project”,建议先阅读这部分内容。 20 | 21 | 无服务架构(Serverless)与微服务架构本身没有继承替代关系,它们并不是同一种层次的架构,无服务的云函数可以作为微服务的一种实现方式,甚至可能是未来很主流的实现方式。在这部文档中我们的话题主要还是聚焦在如何解决分布式架构下的种种问题,相对而言无服务架构并非重点,不过为保证架构演进的完整性,笔者仍然建立了无服务架构的简单演示工程。 22 | 23 | 不过,由于无服务架构原理上就决定了它对程序的启动性能十分敏感,这天生就不利于 Java 程序,尤其不利于 Spring 这类启动时组装的 CDI 框架。因此基于 Java 的程序,除非使用GraalVM 做提前编译、将 Spring 的大部分 Bean 提前初始化,或者迁移至[Quarkus](https://quarkus.io/)这以原生程序为目标的框架上,否则是很难实际用于生产的。 24 | 25 | ## 运行程序 26 | 27 | Serverless 架构的 Fenix's Bookstore 基于[亚马逊 AWS Lambda](https://amazonaws-china.com/cn/lambda/)平台运行,这是最早商用,也是目前全球规模最大的 Serverless 运行平台。从 2018 年开始,中国的主流云服务厂商,如阿里云、腾讯云都推出了各自的 Serverless 云计算环境,如需在这些平台上运行 Fenix's Bookstore,应根据平台提供的 Java SDK 对 StreamLambdaHandler 的代码进行少许调整。 28 | 29 | 假设你已经完成[AWS 注册](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/)、配置[AWS CLI 环境](https://amazonaws-china.com/cn/cli/)以及 IAM 账号的前提下,可通过以下几种途径,可以运行程序,浏览最终的效果: 30 | 31 | - 通过 AWS SAM(Serverless Application Model) Local 在本地运行:
AWS CLI 中附有 SAM CLI,但是版本较旧,可通过[如下地址](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)安装最新版本的 SAM CLI。另外,SAM 需要 Docker 运行环境支持,可参考[此处](/appendix/deployment-env-setup/setup-docker.html)部署。
首先编译应用出二进制包,执行以下标准 Maven 打包命令即可:
32 | 33 | ```bash 34 | $ mvn clean package 35 | ``` 36 | 37 | 根据 pom.xml 中 assembly-zip 的设置,打包将不会生成 SpringBoot Fat JAR,而是产生适用于 AWS Lambda 的 ZIP 包。打包后,确认已在 target 目录生成 ZIP 文件,且文件名称与代码中提供了 sam.yaml 中配置的一致,在工程根目录下运行如下命令启动本地 SAM 测试: 38 | 39 | ```bash 40 | $ sam local start-api --template sam.yaml 41 | ``` 42 | 43 | 在浏览器访问:[http://localhost:3000](http://localhost:3000),系统预置了一个用户(user:icyfenix,pw:123456),也可以注册新用户来测试。 44 | 45 | - 通过 AWS Serverless CLI 将本地 ZIP 包上传至云端运行:
确认已配置 AWS 凭证后,工程中已经提供了 serverless.yml 配置文件,确认文件中 ZIP 的路径与实际 Maven 生成的一致,然后在命令行执行: 46 | 47 | ```bash 48 | $ sls deploy 49 | ``` 50 | 51 | 此时 Serverless CLI 会自动将 ZIP 文件上传至 AWS S3,然后生成对应的 Layers 和 API Gateway,运行结果如下所示: 52 | 53 | ```bash 54 | $ sls deploy 55 | Serverless: Packaging service... 56 | Serverless: Uploading CloudFormation file to S3... 57 | Serverless: Uploading artifacts... 58 | Serverless: Uploading service bookstore-serverless-awslambda-1.0-SNAPSHOT-lambda-package.zip file to S3 (53.58 MB)... 59 | Serverless: Validating template... 60 | Serverless: Updating Stack... 61 | Serverless: Checking Stack update progress... 62 | .............. 63 | Serverless: Stack update finished... 64 | Service Information 65 | service: spring-boot-serverless 66 | stage: dev 67 | region: us-east-1 68 | stack: spring-boot-serverless-dev 69 | resources: 10 70 | api keys: 71 | None 72 | endpoints: 73 | GET - https://cc1oj8hirl.execute-api.us-east-1.amazonaws.com/dev/ 74 | functions: 75 | springBootServerless: spring-boot-serverless-dev-springBootServerless 76 | layers: 77 | None 78 | Serverless: Removing old service artifacts from S3... 79 | ``` 80 | 81 | 访问输出结果中的地址(譬如上面显示的https://cc1oj8hirl.execute-api.us-east-1.amazonaws.com/dev/)即可浏览结果。
需要注意,由于 Serverless 对响应速度的要求本来就较高,所以不建议再采用 HSQLDB 数据库作来运行程序了,每次冷启动都重置一次数据库本身也并不合理。代码中有提供 MySQL 的 Schema,建议采用 AWS RDB MySQL/MariaDB 作为数据库来运行。 82 | 83 | ## 协议 84 | 85 | - 本作品代码部分采用[Apache 2.0 协议](https://www.apache.org/licenses/LICENSE-2.0)进行许可。遵循许可的前提下,你可以自由地对代码进行修改,再发布,可以将代码用作商业用途。但要求你: 86 | 87 | - **署名**:在原有代码和衍生代码中,保留原作者署名及代码来源信息。 88 | - **保留许可证**:在原有代码和衍生代码中,保留 Apache 2.0 协议文件。 89 | 90 | - 本作品文档部分采用[知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/)进行许可。 遵循许可的前提下,你可以自由地共享,包括在任何媒介上以任何形式复制、发行本作品,亦可以自由地演绎、修改、转换或以本作品为基础进行二次创作。但要求你: 91 | - **署名**:应在使用本文档的全部或部分内容时候,注明原作者及来源信息。 92 | - **非商业性使用**:不得用于商业出版或其他任何带有商业性质的行为。如需商业使用,请联系作者。 93 | - **相同方式共享的条件**:在本文档基础上演绎、修改的作品,应当继续以知识共享署名 4.0 国际许可协议进行许可。 94 | -------------------------------------------------------------------------------- /immutable-infrastructure/container/README.md: -------------------------------------------------------------------------------- 1 | # 虚拟化容器 2 | 3 | 容器是云计算、微服务等诸多软件业界核心技术的共同基石,容器的首要目标是让软件分发部署过程从传统的发布安装包、靠人工部署转变为直接发布已经部署好的、包含整套运行环境的虚拟化镜像。在容器技术成熟之前,主流的软件部署过程是由系统管理员编译或下载好二进制安装包,根据软件的部署说明文档准备好正确的操作系统、第三方库、配置文件、资源权限等各种前置依赖以后,才能将程序正确地运行起来。[Chad Fowler](http://chadfowler.com/)在提出“不可变基础设施”这个概念的文章《[Trash Your Servers and Burn Your Code](http://chadfowler.com/2013/06/23/immutable-deployments.html)》里,开篇就直接吐槽:要把一个不知道打过多少个升级补丁,不知道经历了多少任管理员的系统迁移到其他机器上,毫无疑问会是一场灾难。 4 | 5 | 让软件能够在任何环境、任何物理机器上达到“一次编译,到处运行”曾是 Java 早年的宣传口号,这并不是一个简单的目标,不设前提的“到处运行”,仅靠 Java 语言和 Java 虚拟机是不可能达成的,因为一个计算机软件要能够正确运行,需要有以下三方面的兼容性来共同保障(这里仅讨论软件兼容性,不去涉及“如果没有摄像头就无法运行照相程序”这类问题): 6 | 7 | - **ISA 兼容**:目标机器指令集兼容性,譬如 ARM 架构的计算机无法直接运行面向 x86 架构编译的程序。 8 | - **ABI 兼容**:目标系统或者依赖库的二进制兼容性,譬如 Windows 系统环境中无法直接运行 Linux 的程序,又譬如 DirectX 12 的游戏无法运行在 DirectX 9 之上。 9 | - **环境兼容**:目标环境的兼容性,譬如没有正确设置的配置文件、环境变量、注册中心、数据库地址、文件系统的权限等等,任何一个环境因素出现错误,都会让你的程序无法正常运行。 10 | 11 | :::quote 额外知识:ISA 与 ABI 12 | 13 | [指令集架构](https://en.wikipedia.org/wiki/Instruction_set_architecture)(Instruction Set Architecture,ISA)是计算机体系结构中与程序设计有关的部分,包含了基本数据类型,指令集,寄存器,寻址模式,存储体系,中断,异常处理以及外部 I/O。指令集架构包含一系列的 Opcode 操作码(即通常所说的机器语言),以及由特定处理器执行的基本命令。 14 | 15 | [应用二进制接口](https://en.wikipedia.org/wiki/Application_binary_interface)(Application Binary Interface,ABI)是应用程序与操作系统之间或其他依赖库之间的低级接口。ABI 涵盖了各种底层细节,如数据类型的宽度大小、对象的布局、接口调用约定等等。ABI 不同于[应用程序接口](https://en.wikipedia.org/wiki/API)(Application Programming Interface,API),API 定义的是源代码和库之间的接口,因此同样的代码可以在支持这个 API 的任何系统中编译,而 ABI 允许编译好的目标代码在使用兼容 ABI 的系统中无需改动就能直接运行。 16 | 17 | ::: 18 | 19 | 笔者把使用仿真(Emulation)以及虚拟化(Virtualization)技术来解决以上三项兼容性问题的方法都统称为虚拟化技术。根据抽象目标与兼容性高低的不同,虚拟化技术又分为下列五类: 20 | 21 | - **指令集虚拟化**(ISA Level Virtualization)。通过软件来模拟不同 ISA 架构的处理器工作过程,将虚拟机发出的指令转换为符合本机 ISA 的指令,代表为[QEMU](https://www.qemu.org/)和[Bochs](http://bochs.sourceforge.net/)。指令集虚拟化就是仿真,能提供了几乎完全不受局限的兼容性,甚至能做到直接在 Web 浏览器上运行完整操作系统这种令人惊讶的效果,但由于每条指令都要由软件来转换和模拟,它也是性能损失最大的虚拟化技术。 22 | - **硬件抽象层虚拟化**(Hardware Abstraction Level Virtualization)。以软件或者直接通过硬件来模拟处理器、芯片组、内存、磁盘控制器、显卡等设备的工作过程。既可以使用纯软件的二进制翻译来模拟虚拟设备,也可以由硬件的[Intel VT-d](https://en.wikipedia.org/wiki/X86_virtualization#Intel-VT-d)、[AMD-Vi]()这类虚拟化技术,将某个物理设备直通(Passthrough)到虚拟机中使用,代表为[VMware ESXi](https://www.vmware.com/)和[Hyper-V](https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/about/)。如果没有预设语境,一般人们所说的“虚拟机”就是指这一类虚拟化技术。 23 | - **操作系统层虚拟化**(OS Level Virtualization)。无论是指令集虚拟化还是硬件抽象层虚拟化,都会运行一套完全真实的操作系统来解决 ABI 兼容性和环境兼容性问题,虽然 ISA 兼容性是虚拟出来的,但 ABI 兼容性和环境兼容性却是真实存在的。而操作系统层虚拟化则不会提供真实的操作系统,而是采用隔离手段,使得不同进程拥有独立的系统资源和资源配额,看起来仿佛是独享了整个操作系统一般,其实系统的内核仍然是被不同进程所共享的。
操作系统层虚拟化的另一个名字就是本章的主角“容器化”(Containerization),由此可见,容器化仅仅是虚拟化的一个子集,只能提供操作系统内核以上的部分 ABI 兼容性与完整的环境兼容性。这意味着如果没有其他虚拟化手段的辅助,在 Windows 系统上是不可能运行 Linux 的 Docker 镜像的(现在可以,是因为有其他虚拟机或者 WSL2 的支持),反之亦然。也同样决定了如果 Docker 宿主机的内核版本是 Linux Kernel 5.6,那无论上面运行的镜像是 Ubuntu、RHEL、Fedora、Mint 或者任何发行版的镜像,看到的内核一定都是相同的 Linux Kernel 5.6。容器化牺牲了一定的隔离性与兼容性,换来的是比前两种虚拟化更高的启动速度、运行性能和更低的执行负担。 24 | - **运行库虚拟化**(Library Level Virtualization)。与操作系统虚拟化采用隔离手段来模拟系统不同,运行库虚拟化选择使用软件翻译的方法来模拟系统,它以一个独立进程来代替操作系统内核来提供目标软件运行所需的全部能力,这种虚拟化方法获得的 ABI 兼容性高低,取决于软件是否能足够准确和全面地完成翻译工作,其代表为[WINE](https://www.winehq.org/)(Wine Is Not an Emulator 的缩写,一款在 Linux 下运行 Windows 程序的软件)和[WSL](https://docs.microsoft.com/en-us/windows/wsl/about)(特指 Windows Subsystem for Linux Version 1)。 25 | - **语言层虚拟化**(Programming Language Level Virtualization)。由虚拟机将高级语言生成的中间代码转换为目标机器可以直接执行的指令,代表为 Java 的 JVM 和.NET 的 CLR。虽然厂商肯定会提供不同系统下都有相同接口的标准库,但本质上这种虚拟化并不直接解决任何 ABI 兼容性和环境兼容性问题。 26 | -------------------------------------------------------------------------------- /immutable-infrastructure/container/images/controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/container/images/controller.png -------------------------------------------------------------------------------- /immutable-infrastructure/container/images/docker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/container/images/docker.jpg -------------------------------------------------------------------------------- /immutable-infrastructure/container/images/helm-hub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/container/images/helm-hub.png -------------------------------------------------------------------------------- /immutable-infrastructure/container/images/kubernetes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/container/images/kubernetes.png -------------------------------------------------------------------------------- /immutable-infrastructure/container/images/oam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/container/images/oam.png -------------------------------------------------------------------------------- /immutable-infrastructure/container/images/pods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/container/images/pods.png -------------------------------------------------------------------------------- /immutable-infrastructure/container/images/rolling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/container/images/rolling.png -------------------------------------------------------------------------------- /immutable-infrastructure/container/images/runc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/container/images/runc.png -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/README.md: -------------------------------------------------------------------------------- 1 | # 服务网格 2 | 3 | :::tip 服务网格(Service Mesh) 4 | 5 | A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the service mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware. 6 | 7 | 服务网格是一种用于管控服务间通信的的基础设施,职责是为现代云原生应用支持网络请求在复杂的拓扑环境中可靠地传递。在实践中,服务网格通常会以轻量化网络代理的形式来体现,这些代理与应用程序代码会部署在一起,对应用程序来说,它完全不会感知到代理的存在。 8 | 9 | :::right 10 | 11 | —— [What's A Service Mesh? And Why Do I Need One?](https://buoyant.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/),Willian Morgan,Buoyant CEO,2017 12 | 13 | ::: 14 | 15 | 容器编排系统管理的最细粒度只能到达容器层次,在此粒度之下的技术细节,仍然只能依赖程序员自己来管理,编排系统很难提供有效的支持。2016 年,原 Twitter 基础设施工程师 William Morgan 和 Oliver Gould 在 GitHub 上发布了第一代的服务网格产品 Linkerd,并在很短的时间内围绕着 Linkerd 组建了 Buoyant 公司,担任 CEO 的 William Morgan 发表的文章《[What's A Service Mesh? And Why Do I Need One?](https://buoyant.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/)》中首次正式地定义了“服务网格”(Service Mesh)一词,此后,服务网格作为一种新兴通信理念开始迅速传播,越来越频繁地出现在各个公司以及技术社区的视野中。之所以服务网格能够获得企业与社区的重视,就是因为它很好地弥补了容器编排系统对分布式应用细粒度管控能力高不足的缺憾。 16 | 17 | 服务网格并不是什么神秘难以理解的黑科技,它只是一种处理程序间通信的基础设施,典型的存在形式是部署在应用旁边,一对一为应用提供服务的边车代理,及管理这些边车代理的控制程序。“[边车](https://en.wikipedia.org/wiki/Sidecar)”(Sidecar)本来就是一种常见的[容器设计模式](https://www.usenix.org/sites/default/files/conference/protected-files/hotcloud16_slides_burns.pdf),用来形容外挂在容器身上的辅助程序。早在容器盛行以前,边车代理就已有了成功的应用案例,譬如 2014 年开始的[Netflix Prana 项目](https://github.com/Netflix/Prana),由于 Netflix OSS 套件是用 Java 语言开发的,为了让非 JVM 语言的微服务,譬如以 Python、Node.js 编写的程序也同样能接入 Netflix OSS 生态,享受到 Eureka、Ribbon、Hystrix 等框架的支持,Netflix 建立了 Prana 项目,它的作用是为每个服务都提供一个专门的 HTTP Endpoint,使得非 JVM 语言的程序能通过访问该 Endpoint 来获取系统中所有服务的实例、相关路由节点、系统配置参数等在 Netflix 组件中管理的信息。 18 | 19 | Netflix Prana 的代理需要由应用程序主动去访问才能发挥作用,但在容器的刻意支持下,服务网格无需应用程序的任何配合,就能强制性地对应用通信进行管理。它使用了类似网络攻击里中间人流量劫持的手段,完全透明(既无需程序主动访问,也不会被程序感知到)地接管掉容器与外界的通信,将管理的粒度从容器级别细化到了每个单独的远程服务级别,使得基础设施干涉应用程序、介入程序行为的能力大为增强。如此一来,云原生希望用基础设施接管应用程序非功能性需求的目标就能更进一步,从容器粒度延伸到远程访问,分布式系统继容器和容器编排之后,又发掘到另一块更广袤的舞台空间。 20 | -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/images/ebpf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/mesh/images/ebpf.png -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/images/eco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/mesh/images/eco.png -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/images/iptables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/mesh/images/iptables.png -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/images/istio-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/mesh/images/istio-arch.png -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/images/service-mesh-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/mesh/images/service-mesh-1.png -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/images/service-mesh-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/mesh/images/service-mesh-2.png -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/images/service-mesh-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/mesh/images/service-mesh-3.png -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/images/service-mesh-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/mesh/images/service-mesh-4.png -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/images/service-mesh-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/mesh/images/service-mesh-5.png -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/images/smi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/mesh/images/smi.png -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/images/udpa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/mesh/images/udpa.png -------------------------------------------------------------------------------- /immutable-infrastructure/mesh/images/xds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/mesh/images/xds.png -------------------------------------------------------------------------------- /immutable-infrastructure/msa-to-cn.md: -------------------------------------------------------------------------------- 1 | # 从微服务到云原生 2 | 3 | :::tip 云原生定义(Cloud Native Definition) 4 | 5 | Cloud native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach. 6 | 7 | These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil. 8 | 9 | 云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式 API。 10 | 11 | 这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。 12 | 13 | ::: right 14 | 15 | —— [Cloud Native Definition](https://github.com/cncf/toc/blob/master/DEFINITION.md), [CNCF](https://www.cncf.io/),2018 16 | 17 | ::: 18 | 19 | “不可变基础设施”这个概念由来已久。2012 年 Martin Fowler 设想的“[凤凰服务器](https://martinfowler.com/bliki/PhoenixServer.html)”与 2013 年 Chad Fowler 正式提出的“[不可变基础设施](http://chadfowler.com/2013/06/23/immutable-deployments.html),都阐明了基础设施不变性所能带来的益处。在[云原生基金会](https://en.wikipedia.org/wiki/Cloud_Native_Computing_Foundation)定义的“云原生”概念中,“不可变基础设施”提升到了与微服务平级的重要程度,此时它的内涵已不再局限于方便运维、程序升级和部署的手段,而是升华为向应用代码隐藏分布式架构复杂度、让分布式架构得以成为一种可普遍推广的普适架构风格的必要前提。 20 | 21 | 前一章以“分布式的基石”为题,介绍了微服务中关键的技术问题与解决方案,解决这些问题原本应该是架构师与程序员的本职工作。在本章,笔者会以容器、编排系统和服务网格的发展为主线,介绍虚拟化容器与服务网格是如何模糊掉软件与硬件之间的界限,如何在基础设施与通讯层面上帮助微服务隐藏复杂性,解决原本只能由程序员通过软件编程来解决的分布式问题。 22 | -------------------------------------------------------------------------------- /immutable-infrastructure/network/README.md: -------------------------------------------------------------------------------- 1 | # 容器间网络 2 | 3 | 本章我们将会讨论[虚拟化网络](https://en.wikipedia.org/wiki/Network_virtualization)方面的话题,如果不加任何限定,“虚拟化网络”是一项内容十分丰富,研究历史十分悠久的计算机技术,是计算机科学中一门独立的分支,完全不依附于虚拟化容器而存在。网络运营商常提及的“[网络功能虚拟化](https://en.wikipedia.org/wiki/Network_function_virtualization)”(Network Function Virtualization,NFV),网络设备商和网络管理软件提供商常提及的“[软件定义网络](https://en.wikipedia.org/wiki/Software-defined_networking)”(Software Defined Networking,SDN)等都属于虚拟化网络的范畴。对于普通的软件开发者而言,要完全理解和掌握虚拟化网络,需要储备大量开发中不常用到的专业知识与消耗大量的时间成本,一般并无必要。 4 | 5 | 本节我们讨论的虚拟化网络是狭义的,它特指“基于 Linux 系统的网络虚拟化技术来实现的容器间网络通信”,更通俗一点说,就是只关注那些为了相互隔离的 Linux 网络名称空间可相互通信而设计出来的虚拟化网络设施,讨论这个问题所需的网络知识,基本还是在普通开发者应该具有的合理知识范畴之内。在这个语境中的“虚拟化网络”就是直接为容器服务的,说它是依附于容器而存在的亦无不可,因此为避免混淆,笔者在后文中会尽量回避“虚拟化网络”这个范畴过大的概念,后续的讨论将会以“Linux 网络虚拟化”和“容器网络与生态”为题来展开。 6 | -------------------------------------------------------------------------------- /immutable-infrastructure/network/images/bridge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/network/images/bridge.png -------------------------------------------------------------------------------- /immutable-infrastructure/network/images/latency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/network/images/latency.png -------------------------------------------------------------------------------- /immutable-infrastructure/network/images/macvlan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/network/images/macvlan.png -------------------------------------------------------------------------------- /immutable-infrastructure/network/images/msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/network/images/msg.png -------------------------------------------------------------------------------- /immutable-infrastructure/network/images/net-provider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/network/images/net-provider.png -------------------------------------------------------------------------------- /immutable-infrastructure/network/images/netfilter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/network/images/netfilter.png -------------------------------------------------------------------------------- /immutable-infrastructure/network/images/throughtput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/network/images/throughtput.png -------------------------------------------------------------------------------- /immutable-infrastructure/network/images/tun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/network/images/tun.png -------------------------------------------------------------------------------- /immutable-infrastructure/network/images/veth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/network/images/veth.png -------------------------------------------------------------------------------- /immutable-infrastructure/network/images/vlan-router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/network/images/vlan-router.png -------------------------------------------------------------------------------- /immutable-infrastructure/network/images/vxlan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/network/images/vxlan.jpg -------------------------------------------------------------------------------- /immutable-infrastructure/network/plugin.md: -------------------------------------------------------------------------------- 1 | # 虚拟网络生态 2 | 3 | ## Flannel 4 | 5 | ## Calico 6 | 7 | ## Weave 8 | 9 | ## Cilium 10 | 11 | ## Genie -------------------------------------------------------------------------------- /immutable-infrastructure/network/strategy.md: -------------------------------------------------------------------------------- 1 | # 网络策略 2 | 3 | -------------------------------------------------------------------------------- /immutable-infrastructure/schedule/README.md: -------------------------------------------------------------------------------- 1 | # 资源调度 -------------------------------------------------------------------------------- /immutable-infrastructure/schedule/images/2loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/schedule/images/2loop.png -------------------------------------------------------------------------------- /immutable-infrastructure/schedule/images/context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/schedule/images/context.png -------------------------------------------------------------------------------- /immutable-infrastructure/storage/README.md: -------------------------------------------------------------------------------- 1 | # 持久化存储 2 | 3 | 容器是镜像的运行时实例,为了保证镜像能够重复地产生出具备一致性的运行时实例,必须要求镜像本身是持久而稳定的,这决定了在容器中发生的一切数据变动操作都不能真正写入到镜像当中,否则必然会破坏镜像稳定不变的性质。为此,容器中的数据修改操作,大多是基于[写入时复制](https://en.wikipedia.org/wiki/Copy-on-write)(Copy-on-Write)策略来实现的,容器会利用[叠加式文件系统](https://en.wikipedia.org/wiki/OverlayFS)(OverlayFS)的特性,在用户意图对镜像进行修改时,自动将变更的内容写入到独立区域,再与原有数据叠加到一起,使其外观上看来像是“覆盖”了原有内容。这种改动通常都是临时的,一旦容器终止运行,这些存储于独立区域中的变动信息也将被一并移除,不复存在。由此可见,如果不去进行额外的处理,容器默认是不具备持久化存储能力的。 4 | 5 | 而另一方面,容器作为信息系统的运行载体,必定会产生出有价值的、应该被持久保存的信息,譬如扮演数据库角色的容器,大概没有什么系统能够接受数据库像缓存服务一样重启之后会丢失全部数据;多个容器之间也经常需要通过共享存储来实现某些交互操作,譬如[以前](/immutable-infrastructure/container/container-build-system.html)曾经举过的例子,Nginx 容器产生日志、Filebeat 容器收集日志,两者就需要共享同一块日志存储区域才能协同工作。正因为镜像的稳定性与生产数据持久性存在矛盾,由此才产生了本章的主题:如何实现容器的持久化存储。 6 | -------------------------------------------------------------------------------- /immutable-infrastructure/storage/images/aws.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/storage/images/aws.png -------------------------------------------------------------------------------- /immutable-infrastructure/storage/images/csi-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/storage/images/csi-arch.png -------------------------------------------------------------------------------- /immutable-infrastructure/storage/images/csi-protects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/storage/images/csi-protects.png -------------------------------------------------------------------------------- /immutable-infrastructure/storage/images/flexvolume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/storage/images/flexvolume.png -------------------------------------------------------------------------------- /immutable-infrastructure/storage/images/pv-pvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/storage/images/pv-pvc.png -------------------------------------------------------------------------------- /immutable-infrastructure/storage/images/storage-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/storage/images/storage-arch.png -------------------------------------------------------------------------------- /immutable-infrastructure/storage/images/storage-class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/storage/images/storage-class.png -------------------------------------------------------------------------------- /immutable-infrastructure/storage/images/types-of-mounts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/storage/images/types-of-mounts.png -------------------------------------------------------------------------------- /immutable-infrastructure/storage/images/v-pv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/storage/images/v-pv.png -------------------------------------------------------------------------------- /immutable-infrastructure/storage/images/volume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/immutable-infrastructure/storage/images/volume.png -------------------------------------------------------------------------------- /immutable-infrastructure/storage/storage-plugins.md: -------------------------------------------------------------------------------- 1 | # 共享存储插件 2 | 3 | -------------------------------------------------------------------------------- /introduction/about-book.md: -------------------------------------------------------------------------------- 1 | # 关于纸质书 2 | 3 |
4 | 5 | :::center 6 | ![凤凰架构](./images/book.png) 7 | 《凤凰架构:构建可靠的大型分布式系统》 8 | 9 | 衷心感谢在本书撰写过程中,各位读者在本站的建议、意见、讨论、勘误指正与鼓励支持。
如果本书对您有帮助,到豆瓣或者GitHub上给予点赞、评价是对作者最好的支持。 10 | 11 | 12 | 13 | 出版信息:2021 年 6 月 / 机械工业出版社
14 | 页数定价:409 页 / 33 万字 / 99 元
15 | 购买链接:[京东商城](https://item.jd.com/12880681.html) | [当当书城](http://product.dangdang.com/29265341.html) | [天猫商城](https://m.tb.cn/h.4EqXGEs?sm=b36ed1)
16 | 其他相关:[豆瓣评价](https://book.douban.com/subject/35492898/) | [音频版公开课](https://time.geekbang.org/opencourse/intro/100064201) | [勘误](https://github.com/fenixsoft/fenix_architecture_book)
17 | 18 | 19 | ::: 20 | -------------------------------------------------------------------------------- /introduction/about-me.md: -------------------------------------------------------------------------------- 1 | # 关于作者 2 | 3 | 周志明 4 | 5 | 6 | 7 | 8 | 9 | 10 | Ph.D、Full Stack Programmer、Computer Book Writer、Technical Evangelist、Cloud Native Architect、Most Valuable Professional、HLLVM/PLDI Enthusiast 11 | 12 |
13 | 14 | - 程序员
15 | 华为七级专家,兼职一些管理与研究工作的程序员。主要从事大型企业级软件的架构与研发工作,参与过 MetaERP、MetaCRM 等重要变革项目,分别担任首席技术规划与总架构师。业余里对计算机科学相关的多个领域都有持续跟进。 16 | 17 | - 研究员
18 | 理学博士,华为企业应用教研室主任。曾任远光软件研究院院长,澳门科大-远光人工智能联合实验室主任,研究方向为机器学习自动化特征选择。 19 | 20 | - 计算机技术作家
出版过八部计算机技术书籍,撰写过两部开源文档,口碑和销量均得到业内认可。其中五本书在[豆瓣](https://www.douban.com/)上获得了 9.0 分及以上的评价,“深入理解 Java 虚拟机”系列重印超过 45 次,销量逾 40 万册。 21 | - 2021 年 《[凤凰架构:构建可靠的大型分布式系统](https://icyfenix.cn/introduction/about-book.html)》(豆瓣 9.4) 22 | - 2020 年 《[软件架构探索:The Fenix Project](https://icyfenix.cn/)》 (开源文档) 23 | - 2019 年 《[深入理解 Java 虚拟机:JVM 高级特性与最佳实践(第三版)](https://book.douban.com/subject/34907497/)》(豆瓣 9.5) 24 | - 2018 年 《[智慧的疆界:从图灵机到人工智能](https://book.douban.com/subject/30379536/)》(豆瓣 9.4) 25 | - 2016 年 《[深入理解 Java 虚拟机:JVM 高级特性与最佳实践(第二版)](https://book.douban.com/subject/24722612/)》(豆瓣 9.1) 26 | - 2015 年 《[Java 虚拟机规范(Java SE 8 中文版)](https://book.douban.com/subject/26418340/)》(官方授权翻译,豆瓣 8.4) 27 | - 2014 年 《[Java 虚拟机规范(Java SE 7 中文版)](https://book.douban.com/subject/25792515/)》(官方授权翻译,豆瓣 9.0) 28 | - 2013 年 《[深入理解 OSGi:Equinox 原理、应用与最佳实践](https://book.douban.com/subject/21324330/)》(豆瓣 7.7) 29 | - 2011 年 《[深入理解 Java 虚拟机:JVM 高级特性与最佳实践(第一版)](https://book.douban.com/subject/6522893/)》(豆瓣 8.6) 30 | - 2011 年 《[Java 虚拟机规范(Java SE 7 中文版)](https://www.iteye.com/topic/1117824)》 (开源文档) 31 | 32 | - 技术布道师
33 | 开源技术的积极倡导者和推动者,国内主流云计算厂商的最有价值技术专家,媒体撰稿人,会议讲师。 34 | - [阿里云最有价值技术专家(MVP)](https://mvp.aliyun.com/mvp/detail/487) 35 | - [腾讯云最有价值技术专家(TVP)](https://cloud.tencent.com/tvp/132) 36 | - [华为云最有价值技术专家(MVP)](https://developer.huaweicloud.com/mvp/member) 37 | - [IBM DeveloperWorks 撰稿人]()、[InfoQ.CN 专栏撰稿人](https://www.infoq.cn/profile/CD59DD20F93F11/publish) 38 | - [Java 核心技术大会会议主席](https://ke.segmentfault.com/course/1650000041954414)、[极客时间布道师](https://time.geekbang.org/opencourse/intro/100064201)、[华章课程讲师](https://xie.infoq.cn/article/36ec9efa0697377af0d043b1e) 39 | - [QCon 全球软件开发大会主题明星讲师](https://qcon.infoq.cn/2020/shenzhen/)、[ArchSummit 全球架构师峰会主题演讲嘉宾](https://archsummit.infoq.cn/2021/shenzhen/presentation/4104)、[HuaweiConnect 华为全连接大会专题讲师](https://www.huawei.com/cn/events/huaweiconnect/agenda?type=%E4%B8%93%E9%A2%98%E6%BC%94%E8%AE%B2) 40 | 41 |
42 | 43 | :::not-print 44 | 45 | 46 | 47 | 48 | 49 | 50 | ::: 51 | -------------------------------------------------------------------------------- /introduction/about-the-fenix-project.md: -------------------------------------------------------------------------------- 1 | # 什么是“凤凰架构” 2 | 3 | “Phoenix”这个词东方人不常用,但在西方的软件工程读物——尤其是关于 Agile、DevOps 话题的作品中时常出现。软件工程小说《[The Phoenix Project](https://book.douban.com/subject/20644908/)》讲述了徘徊在死亡边缘的 Phoenix 项目在精益方法下浴火重生的故事;马丁·福勒(Martin Fowler)对《[Continuous Delivery](https://book.douban.com/subject/4327796/)》的诠释里,曾多次提到“[Phoenix Server](https://martinfowler.com/bliki/PhoenixServer.html)”(取其能够“涅槃重生”之意)与“[Snowflake Server](https://martinfowler.com/bliki/SnowflakeServer.html)”(取其“世界上没有相同的两片雪花”之意)的优劣比对。也许是东西方的文化的差异,尽管有“失败是成功之母”这样的谚语,但我们东方人的骨子里更注重的还是一次把事做对做好,尽量别出乱子;而西方人则要“更看得开”一些,把出错看做正常甚至是必须的发展过程,只要出了问题能够兜底使其重回正轨便好。 4 | 5 |
6 | 7 | The Phoenix Project 8 |
9 | 10 | 在软件工程里,任何产品的研发,只要时间尺度足够长,人就总会疏忽犯错,代码就总会携有缺陷,电脑就总会宕机崩溃,网络就总会堵塞中断……如果一项工程需要大量的人员,共同去研发某个大规模的软件产品,并使其分布在网络中大量的服务器节点中同时运行,随着项目规模的增大、运作时间变长,其必然会受到墨菲定律的无情打击。 11 | 12 | :::quote 墨菲定律(Murphy's Law) 13 | Anything that can go wrong will go wrong.
如果事情可能出错就总会出错。 14 | :::right 15 | —— [Nevil Maskelyne](),1908 16 | ::: 17 | 18 | 为了得到高质量的软件产品,我们是应该把精力更多地集中在提升其中每一个人员、过程、产出物的能力和质量上,还是该把更多精力放在整体流程和架构上? 19 | 20 | 笔者先给这个问题一个“和稀泥”式的回答:这两者都重要。前者重术,后者重道;前者更多与编码能力相关,后者更多与软件架构相关;前者主要由开发者个体水平决定,后者主要由技术决策者水平决定; 21 | 22 | 然而,笔者也必须强调此问题的另外一面:这两者的理解路径和抽象程度是不一样的。如何学习一项具体的语言、框架、工具,譬如 Java、Spring、Vue.js……都是相对具象的,不论其蕴含的内容多少,复杂程度高低,它是至少能看得见摸得着。而如何学习某一种风格的架构方法,譬如单体、微服务、服务网格、无服务、云原生……则是相对抽象的,谈论它们可能要面临着“一百个人眼中有一百个哈姆雷特”的困境。谈这方面的话题,若要言之有物,就不能是单纯的经验陈述。笔者想来,回到这些架构根本的出发点和问题上,真正去使用这些不同风格的架构方法来实现某些需求,解决某些问题,然后在实践中观察它们的异同优劣,会是一种很好的,也许是最好的讲述方式。笔者想说一下这些架构,而且还想说得透彻明白,这需要代码与文字的配合,于是便有了这个项目。 23 | 24 | ## 可靠的系统 25 | 26 | 让我们再来思考一个问题,构建一个大规模但依然可靠的软件系统,是否是可行的? 27 | 28 | 这个问题令人听起来的第一感觉也许会有点荒谬:废话。如果这个事情从理论上来说就是根本不可能的话,那我们这些软件开发从业人员现在还在瞎忙活些什么?但你再仔细想想,前面才提到的“墨菲定律”和在“大规模”这个前提下必然会遇到的各种不靠谱的人员、代码、硬件、网络等因素,从中能得出的一个听起来颇为符合逻辑直觉的推论:如果一项工作要经过多个“不靠谱”的过程相互协作来完成,其中的误差应会不断地累积叠加,导致最终结果必然不能收敛稳定才对。 29 | 30 | 这个问题也并非杞人忧天庸人自扰式的瞎操心,计算机之父冯·诺依曼(John von Neumann)在 1940 年代末期,曾经花费了大约两年时间,研究这个问题并且得出了一门理论《[自复制自动机](https://en.wikipedia.org/wiki/Self-replicating_machine)》(Theory of Self-Reproducing Automata),这个理论以机器应该如何从基本的部件中构造出与自身相同的另一台机器引出,其目的并不是想单纯地模拟或者理解生物体的自我复制,也并不是简单想制造自我复制的计算机,他的最终目的就是想回答一个理论问题:如何用一些不可靠的部件来构造出一个可靠的系统。 31 | 32 |
33 | 34 | 当时自复制机的艺术表示(图片来自维基百科) 35 |
36 | 37 | 自复制机恰好就是一个最好的用不可靠部件构造的可靠的系统例子。这里,“不可靠部件”可以理解为构成生命的大量细胞、甚至是分子。由于热力学扰动、生物复制差错等因素干扰,这些分子本身并不可靠。但是生命系统之所以可靠的本质,恰是因为它可以使用不可靠的部件来完成遗传迭代。这其中的关键点便是承认细胞等这些零部件可能会出错,某个具体的零部件可能会崩溃消亡,但在存续生命的微生态系统中一定会有其后代的出现,重新代替该零部件的作用,以维持系统的整体稳定。在这个微生态里,每一个部件都可以看作一只不死鸟(Phoenix),它会老迈,而之后又能涅槃重生。 38 | 39 | ## 架构的演进 40 | 41 | 软件架构风格从大型机(Mainframe),到[原始分布式](/architecture/architect-history/primitive-distribution.html)(Distributed),到[大型单体](/architecture/architect-history/monolithic.html)(Monolithic),到[面向服务](/architecture/architect-history/soa.html)(Service-Oriented),到[微服务](/architecture/architect-history/microservices.html)(Microservices),到[服务网格](/architecture/architect-history/post-microservices.html)(Service Mesh),到[无服务](/architecture/architect-history/serverless.html)(Serverless)……技术架构上确实呈现出“从大到小”的发展趋势。当近年来微服务兴起以后,涌现出各类文章去总结、赞美微服务带来的种种好处,诸如简化部署、逻辑拆分更清晰、便于技术异构、易于伸缩拓展应对更高的性能等等,这些当然都是重要优点和动力。可是,如果不拘泥于特定系统或特定某个问题,以更宏观的角度来看,前面所列这种种好处却都只能算是“锦上添花”、是属于让系统“活得更好”的动因,肯定比不上系统如何“确保生存”的需求来得关键、本质。在笔者看来,架构演变最重要的驱动力,或者说这种“从大到小”趋势的最根本的驱动力,始终都是为了方便某个服务能够顺利地“死去”与“重生”而设计的,个体服务的生死更迭,是关系到整个系统能否可靠续存的关键因素。 42 | 43 | 举个例子,譬如某企业中应用的单体架构的 Java 系统,其更新、升级都必须要有固定的停机计划,必须在特定的时间窗口内才能按时开始,必须按时结束。如果出现了非计划的宕机,那便是生产事故。但是软件的缺陷不会遵循领导定下的停机计划来“安排时间出错”,为了应对缺陷与变化,做到不停机地检修,Java 曾经搞出了 OSGi 和 JVMTI Instrumentation 等这样复杂的 HotSwap 方案,以实现给奔跑中的汽车更换轮胎这种匪夷所思却又无可奈何的需求;而在微服务架构的视角下,所谓系统检修,不过只是一次在线服务更新而已,先停掉 1/3 的机器,升级新的软件版本,再有条不紊地导流、测试、做金丝雀发布,一切都是显得如此理所当然、平淡寻常;而在无服务架构的视角下,我们甚至都不可能去关心服务所运行的基础设施,连机器是哪台都不必知道,停机升级什么的就根本无从谈起了。 44 | 45 | 流水不腐,有老朽,有消亡,有重生,有更迭才是生态运行的合理规律。请设想一下,如果你的系统中每个部件都符合“Phoenix”的特性,哪怕其中某些部件采用了由极不靠谱的人员所开发的极不靠谱程序代码,哪怕存有严重的内存泄漏问题,最多只能服务三分钟就一定会崩溃。而即便这样,只要在整体架构设计有恰当的、自动化的错误熔断、服务淘汰和重建的机制,在系统外部来观察,整体上仍然有可能表现出稳定和健壮的服务能力。 46 | 47 | ## 凤凰架构 48 | 49 | 在企业软件开发的历史中,一项新技术发布时,常有伴以该技术开发的”宠物店(PetStore)”作为演示的传统(如[J2EE PetStore](https://www.oracle.com/technetwork/java/petstore1-3-1-02-139690.html)、[.NET PetShop](https://archive.codeplex.com/?p=petshopmvc)、[Spring PetClinic](https://github.com/spring-projects/spring-petclinic)等等)。作为不同架构风格的演示时,笔者本也希望能遵循此传统,却无奈从来没养过宠物,遂改行开了书店(Fenix's Bookstore),里面出售了几本笔者撰写过的书籍,算是夹带一点私货,同时也避免了使用素材时可能的版权问题。 50 | 51 | 尽管相信没有人会误解,但笔者最后还是多强调一句,Oracle、Microsoft、Pivotal 等公司设计宠物店的目的绝不是为了日后能在网上贩卖小猫小狗,只是纯粹的演示技术。所以也请勿以“实现这种学生毕业设计复杂度的需求,引入如此规模的架构或框架,纯属大炮打苍蝇,肯定是过度设计”的眼光来看待接下来的“Fenix's Bookstore”项目。相反,如果可能的话,笔者会在有新的技术、框架发布出来时,持续更新,以恰当的形式添加到项目的不同版本中,可能使其技术栈越来越复杂。笔者希望把这些新的、不断发展的知识,融合进已有的知识框架之中,让自己学习、理解、思考,然后将这些技术连同自己的观点看法,传播给感兴趣的人。 52 | 53 | 也算是缘分,网名“IcyFenix”在二十多年前我的中学时代开始使用,最初它是来源于暴雪公司的即时战略游戏《星际争霸》的 Protoss 英雄[Fenix](https://starcraft.fandom.com/wiki/Fenix)——如名字所预示的那样,他曾经是 Zealot,牺牲后以 Dragoon 的形式重生,带领 Protoss 与刀锋女王 Kerrigan 继续抗争。尽管中学时期我已经笃定自己未来肯定会从事信息技术相关的工作,但显然不可能预计到二十年后我会写下这些文字。 54 | 55 | 所以,既然我们要开始一段关于“Phoenix”的架构讨论与代码实践,那便叫它“凤凰架构”,如何? 56 | -------------------------------------------------------------------------------- /introduction/images/book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/introduction/images/book.png -------------------------------------------------------------------------------- /introduction/images/github-brands.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /introduction/images/icyfenix.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/introduction/images/icyfenix.jpg -------------------------------------------------------------------------------- /introduction/images/icyfenix2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/introduction/images/icyfenix2.jpg -------------------------------------------------------------------------------- /introduction/images/icyfenix3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/introduction/images/icyfenix3.jpg -------------------------------------------------------------------------------- /introduction/images/icyfenix4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/introduction/images/icyfenix4.jpg -------------------------------------------------------------------------------- /introduction/images/linkedin-brands.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /introduction/images/mail-bulk-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /introduction/images/self-reproducing-automata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/introduction/images/self-reproducing-automata.png -------------------------------------------------------------------------------- /introduction/images/the-phoenix-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/introduction/images/the-phoenix-project.png -------------------------------------------------------------------------------- /introduction/images/weibo-brands.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /methodology/forward-msa/README.md: -------------------------------------------------------------------------------- 1 | # 向微服务迈进 2 | 3 | :::tip 没有银弹 4 | 5 | 传说里,能从普通人忽然变身的狼人是梦靥中最为可怖的怪物,人们一直尝试寻找到能对狼人一枪毙命的银弹。 6 | 7 | 软件亦有着狼人的特性,平常看似人畜无害的技术研发工作,转眼间就能变成一只工期延误、预算超支、产品满身瑕疵的怪兽。我听到了管理者、程序员与用户都在绝望地呼唤,大家都渴望能找到某种可以有效降低软件开发的成本的银弹,让软件开发的成本也能如同电脑硬件的成本那样,稳定且快速地下降。 8 | 9 | :::right 10 | 11 | —— [Fred Brooks](https://en.wikipedia.org/wiki/Fred_Brooks),[No Silver Bullet:Essence and Accidents of Software Engineering](https://en.wikipedia.org/wiki/No_Silver_Bullet), 1987 12 | 13 | ::: 14 | 15 | 这部文档的主体内容是务实的,多谈具体技术,少谈方向理论。只在本章中会集中讨论几点与分布式、微服务、架构等相关的相对务虚的话题。 16 | 17 | IBM 大型机之父[Fred Brooks](https://en.wikipedia.org/wiki/Fred_Brooks) 在他的两本著作《[没有银弹:软件工程的本质性与附属性工作](https://en.wikipedia.org/wiki/No_Silver_Bullet)》和《[人月神话:软件项目管理之道](https://en.wikipedia.org/wiki/The_Mythical_Man-Month)》里都反复强调着一个观点:“**软件研发中任何一项技术、方法、架构都不可能是银弹**”,这个结论已经被软件工程里无数事实所验证,现在对于微服务也依然成立。本节,笔者将会谈到哪些场景适合去使用微服务,以及一些已经被验证过、被总结为经验的最佳的实践方式;而更主要的是想讨论什么场景不适合微服务,微服务存在哪些理解误区、应用前提,等等。 18 | 19 | 作为一部技术文档的作者,如果有同学是因为看了此文档,然后被带进微服务的沟里,那作者只强调一句“微服务不是银弹”也难以免责,所以,在你准备发起实际行动向微服务迈进前,希望你能阅读一遍本章,向微服务迈进——的避坑指南。 20 | -------------------------------------------------------------------------------- /methodology/forward-msa/granularity.md: -------------------------------------------------------------------------------- 1 | # 边界:微服务的粒度 2 | 3 | :::tip 勿行极端,过犹不及 4 | 5 | 子贡问:“师与商也孰贤?” 子曰:“师也过,商也不及。” 曰:“然则师愈与?” 子曰:“过犹不及。” 6 | 7 | 子贡问:“颛孙师和卜商谁更贤德?” 孔子说:“颛孙师常常作得有些过头,卜商常常达不到要求。” 子贡说:“如此说来,是不是颛孙师要好一些呢?” 孔子说:“过头和达不到同样不好。” 8 | 9 | :::right 10 | 11 | —— 论语·先进 12 | 13 | ::: 14 | 15 | 当今软件业界,对本节的话题“识别微服务的边界”其实已取得了较为一致的观点,也找到了指导具体实践的方法论,即[领域驱动设计](https://en.wikipedia.org/wiki/Domain-driven_design)(Domain-Driven Design,DDD)。囿于主题,在这部文档中甚少涉及该如何抽象业务、分析流程、识别边界、建立模型、映射到服务和代码等偏重理论的务虚话题,即使在这一章中,笔者也尽量规避了 DDD 中需要专门学习才能理解的概念,如界限上下文(Bounded Context)、语境映射(Context Map)、通用语言(Ubiquitous Language)、领域和子域(Domain、Sub Domain)、聚合(Aggregate)、领域事件(Domain Event)等等。并非笔者认为业务流程与设计方法论不重要,而是如果要严谨、深刻地讨论这些话题,其篇幅足以独立地写出一本书。事实上,市场上已经有不少这样的书了,DDD 的发明人 Eric Evans 撰写的同名书籍《[领域驱动设计:软件核心复杂性应对之道](https://book.douban.com/subject/5344973/)》便是其中翘楚。笔者个人是更推荐 Chris Richardson 撰写的颇具口碑的入门书《[微服务架构设计模式](https://book.douban.com/subject/33425123/)》,其叙述的主线就是在 DDD 指导下,如何将一个单体服务逐步拆分为微服务结构,如果你对这方面感兴趣,不妨一读。这两节中,笔者会从业务之外的其他角度,从非功能性、研发效率等方面来探讨微服务的粒度与拆分。 16 | 17 | 系统设计是一种创作,而不是应试,不可能每一位架构师设计的服务粒度全都相同,微服务的大小、边界不应该只有唯一正确的答案或绝对的标准,但是应该有个合理的范围,笔者称其为微服务粒度的上下界。我们可以分析如果微服务的粒度太小或者太大会出现哪些问题,从而得出服务上下界应该定在哪里。 18 | 19 | 可能是受微服务名字中“微”的“蛊惑”,笔者听过不少人提倡过微服务越小越好,最好做到一个 REST Endpoint 就对应于一个微服务,这种极端的理解肯定是错误的,如果将微服务粒度定的过细,会受到以下几个方面的反噬: 20 | 21 | - 从性能角度看,一次进程内的方法调用(仅计算调用,与方法具体内容无关),耗时在零(按方法完全内联的场景来计算)到数百个[时钟周期](https://en.wikipedia.org/wiki/Cycles_per_instruction)(按最慢的虚方法调用无内联缓存要查虚表的场景来计算)之间;一次跨服务的方法调用里,网络传输、参数序列化和结果反序列化都是不可避免的,耗时要达到毫秒级别,你可以算一下这两者有多少个数量级的差距。[远程服务调用](/architect-perspective/general-architecture/api-style/rpc.html)里已经解释了“透明的分布式通信”是不存在的,因此,服务粒度大小必须考虑到消耗在网络上的时间与方法本身执行时间的比例,避免设计得的过于琐碎,客户端不得不多次调用服务才能完成一项业务操作,譬如,将字符串处理这样的功能设计为一个微服务便是不合适的,这点要求微服务从功能设计上看应该是完备的。 22 | 23 | - 从数据一致性角度看,每个微服务都有自己独立的数据源,如果多个微服务要协同工作,我们可以采用[很多办法](/architect-perspective/general-architecture/transaction/distributed.html)来保证它们处理数据的最终一致性,但如果某些数据必须要求保证强一致性的话,那它们本身就应当聚合在同一个微服务中,而不是强行启用[XA 事务](/architect-perspective/general-architecture/transaction/global.html)来实现,因为参与协作的微服务越多,XA 事务的可用性就越差,这点要求微服务从数据一致性上看应该是[内聚]()(Cohesion)的。 24 | - 从服务可用性角度看,服务之间是松散耦合的依赖关系,微服务架构中无法也不应该假设被调用的服务具有绝对的可用性,服务可能因为网络分区、软件重启升级、硬件故障等任何原因发生中断。如果两个微服务都必须依赖对方可用才能正常工作,那就应当将其合并到同一个微服务中(注意这里说的是“彼此依赖对方才能工作”,单向的依赖是必定存在的),这条要求微服务从依赖关系上看应该是独立的。 25 | 26 | 综合以上,我们可以得出第一个结论:**微服务粒度的下界是它至少应满足独立——能够独立发布、独立部署、独立运行与独立测试,内聚——强相关的功能与数据在同一个服务中处理,完备——一个服务包含至少一项业务实体与对应的完整操作。** 27 | 28 | 我们再来想想,如果微服务的粒度太大,会出现什么问题?从技术角度讲,并不会有什么问题,每个能正常工作的单体系统都能满足独立、内聚、完备的要求,世界上又有那么多运行良好的单体系统。微服务的上界并非受限于技术,而是受限于人,更准确地说,受限于人与人之间的社交协作。《人月神话》中最反直觉的一个结论是:“为进度给项目增加人力,如同用水去为油锅灭火”(Adding Manpower to A Late Software Project Makes It Later)。为什么?Fred Brooks 给出了简洁而有力的答案: 29 | 30 | :::center 31 | 32 | > 软件项目中的沟通成本= n×(n-1)/2,n 为参与项目的人数 33 | 34 | ::: 35 | 36 | 为了让你能更直观地理解这个答案,笔者已经算好了一组数字:15 人参与的项目,沟通成本大约是 5 个人时的十倍,150 人参与的项目,沟通成本大约是 5 个人时的一千倍。你不妨回想一下自己在公司的工作体验,不可能有 150 人的团队而不划分出独立小组来管理的,除非这些人都从事流水线式的工作,协作时完全不需要沟通。此外,你也不妨回想一下自己的生活体验,我敢断言你的社交上界是不超过 5 个知己好友,15 个可信任的伙伴,35 个普通朋友,150 个说得上话的人。这句话的信心底气源于此观点是人类学家[Robin Dunbar](https://en.wikipedia.org/wiki/Robin_Dunbar)在 1992 年给出的科学结论,今天已被普遍认可,被称为“邓巴数”(Dunbar's Number),据说是人脑的[新皮质](https://en.wikipedia.org/wiki/Neocortex)大小限制了人能承受的社交数量,决定了邓巴数这个社交的上界。 37 | 38 | 有了以上铺垫,你应该更能理解前面的许多文章中笔者为何采用“2 Pizza Team”作为微服务团队规模的“量词”了,并不是因为制造这个梗的人是[Jeff Bezos](https://en.wikipedia.org/wiki/Jeff_Bezos),是亚马逊 CEO、世界首富。而是因为两个 Pizza 能喂饱的人数大概就是 6-12 人,符合软件开发中团队管理的理想规模。 39 | 40 | 康威定律约束了软件的架构与组织的架构要保持一致,所以微服务的上界应该与 2 Pizza Team 能够开发的最大程序规模保持一致。2 Pizza Team 能开发多大规模的程序?人员数量固定的前提下,这个答案不仅与开发者的能力水平相关,更是与研发模式和周期相关。如果你的软件产品是瀑布开发,可能需要一个月、两个月迭代一次;如果采用 Scrum,可能会一周、两周完成一次冲刺;如果追求日构建、精益,甚至可能一天、两天就会集成构建出一个小版本,以上不同的研发方法,都会产生相应规模的上界。 41 | 42 | 综合以上,我们得出了第二个结论:**微服务粒度的上界是一个 2 Pizza Team 能够在一个研发周期内完成的全部需求范围。** 43 | 44 | 在上下界范围内,架构师会根据业务和团队的实际情况来灵活划定微服务的具体粒度。譬如下界的完备性要求微服务至少包含一项完整的服务,不超过上界的前提下,这个微服务包含了两项、三项业务操作是否合理,那需要根据这些操作本身是否有合理的逻辑关系来具体讨论。又譬如上界要求单个研发周期内能处理掉一个微服务的全部需求,不超过下界的前提下,一个周期就能完成分属于两个、三个微服务的全部需求时,是缩短研发周期更合理,还是允许这个周期内同时开发几个微服务,也可以根据实际情况具体讨论。 45 | -------------------------------------------------------------------------------- /methodology/forward-msa/images/autonomous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/methodology/forward-msa/images/autonomous.png -------------------------------------------------------------------------------- /methodology/forward-msa/images/evolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/methodology/forward-msa/images/evolution.png -------------------------------------------------------------------------------- /methodology/forward-msa/images/line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/methodology/forward-msa/images/line.png -------------------------------------------------------------------------------- /methodology/forward-msa/images/ms.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/methodology/forward-msa/images/ms.jpg -------------------------------------------------------------------------------- /methodology/forward-msa/measurement.md: -------------------------------------------------------------------------------- 1 | 2 | # 度量:以结果驱动开发 3 | 4 | -------------------------------------------------------------------------------- /methodology/forward-msa/objective.md: -------------------------------------------------------------------------------- 1 | # 目的:微服务的驱动力 2 | 3 | :::tip 微服务的目的 4 | 5 | The goal of microservices is to sufficiently decompose the application in order to facilitate agile application development and deployment. 6 | 7 | 微服务的目的是有效的拆分应用,实现敏捷开发和部署。 8 | 9 | :::right 10 | 11 | —— [Chris Richardson](https://www.nginx.com/people/chris-richardson/), Founder of CloudFoundry, [Introduction to Microservices](https://www.nginx.com/blog/introduction-to-microservices/) 12 | 13 | ::: 14 | 15 | 在讨论什么时候开始、以及如何向微服务迁移之前,我们先来理清为什么需要微服务。凡事总该先有目的,有预期收益再谈行动才显得合理。有人会说迈向微服务的目的是为了追求更先进的架构形式。这话对,但没有什么信息量可言,任何一次架构演进的目的都是为了更加先进,应该没谁是为“追求落后”而重构系统的。有人会说微服务是信息系统发展的必然阶段,为了应对日益庞大的压力,获得更好的性能,自然会演进至能够扩缩自如的微服务架构,这个观点看似合理、具体、正确,实则争议颇大。笔者个人的态度是旗帜鲜明地反对以“获得更好的性能”为主要目的,将系统重构为微服务架构的,性能有可能会作为辅助性的理由,但仅仅为了性能而选择分布式的话,那应该是 40 年前“[原始分布式时代](/architecture/architect-history/primitive-distribution.html)”所追求的目标。现代的单体系统同样会采用可扩缩的设计,同样能够集群部署,更重要的是云计算数据中心的处理能力几乎可以认为是无限的,那能够通过扩展硬件的手段解决问题就尽量别使用复杂的软件方法,其中原因在前面引用的《没有银弹》中已经解释过:**硬件的成本能够持续稳定地下降,而软件开发的成本则不可能**。而且,性能也不会因为采用了微服务架构而凭空产生。把系统拆成多个微服务,一旦在某个关键地方依然卡住了业务流程,其整体的结果往往还不如单体,没有清晰的职责划分,导致扩展性失效,多加机器往往还不如单机。前面这句话将性能替换为代码质量、生产力等词语往往也同样适用,这些方面笔者就不再赘述了。 16 | 17 | 软件系统选择微服务架构,通常比较常见的、合理的驱动力来自组织外部、内部两方面,笔者先列举一些外部因素: 18 | 19 | - **当意识到没有什么技术能够包打天下。**
举个具体例子,某个系统选用了处于[Tiobe 排行榜](https://www.tiobe.com/tiobe-index/)榜首多年的 Java 语言来开发,也会遇到很多想做但 Java 却不擅长的事情。譬如想去做人工智能,进行深度学习训练,发现大量的库和开源代码都离不开 Python;想要引入分布式协调工具时,发现近几年 ZooKeeper 已经有被后起之秀 Golang 的 Etcd 蚕食替代的趋势;想要做集中式缓存,发现无可争议的首选是 ANSI C 编写的 Redis,等等。很多时候为异构能力进行的分布式部署,并不是你想或者不想的问题,而是没有选择、无可避免的。 20 | - **当个人能力因素成为系统发展的明显制约。**
对于北上广深的信息技术企业这个问题可能不会成为主要矛盾,在其他地区,不少软件公司即使有钱也很难招到大量的靠谱的高端开发者。此时,无论是引入外包团队,抑或是让少量技术专家带着大量普通水平的开发者去共同完成一个大型系统,微服务都是一个更有潜力的选择。在单体架构下,没有什么有效阻断错误传播的手段,系统中“整体”与“部分”的关系没有物理的划分,系统质量只能靠研发与项目管理措施来尽可能地保障,少量的技术专家很难阻止大量螺丝钉式的程序员或者不熟悉原有技术架构的外包人员在某个不起眼的地方犯错并产生全局性的影响,不容易做出整体可靠的大型系统。这时微服务可以作为专家掌控架构约束力的技术手段,由高水平的开发、运维人员去保证关键的技术和业务服务靠谱,其他大量外围的功能即使不靠谱,甚至默认它们必定不靠谱,也能保证系统整体的稳定和局部的容错、自愈与快速迭代。 21 | - **当遇到来自外部商业层面对内部技术层面提出的要求。**
对于那些以“自产自销”为主的互联网公司来说这一点体验不明显,但对于很多为企业提供信息服务的软件公司来说,甲方爸爸的要求往往才是具决定性的推动力。技术、需求上困难也许能变通克服,但当微服务架构变成大型系统先进性的背书时,甲方的招投标文件技术规范明文要求系统必须支持微服务架构、支持分布式部署,那就没有多少讨价还价的余地。 22 | - …… 23 | 24 | 在系统和研发团队内部,也会有一些因素促使其向微服务靠拢: 25 | 26 | - **变化发展特别快的创新业务系统往往会自主地向微服务架构靠近。**
需求喊着“要试错!要创新!要拥抱变化!”,开发喊着“资源永远不够!活干不完!”,运维喊着“你见过凌晨四点的洛杉矶吗!”,对于那种“一个功能上线平均活不过三天”的系统,如果团队本身能力能够支撑在合理的代价下让功能有快速迭代的可能,让代码能避免在类库层面的直接依赖而导致纠缠不清,让系统有更好的可观测性和回弹性(自愈能力),需求、开发、运维肯定都是很乐意接受微服务的,毕竟此时大家的利益一致,微服务的实施也会水到渠成。 27 | 28 | - **大规模的、业务复杂的、历史包袱沉重的系统也可能主动向微服务架构靠近。**
这类系统最后的结局不外乎三种:
第一种是日渐臃肿,客户忍了,系统持续维持着,直到谁也替代不了却又谁也维护不了。笔者曾听说过国外有公司招聘 60、70 岁的爷爷辈程序员去维护上个世纪的 COBOL 编写的系统,没有求证过这到底是网络段子还是确有其事。
第二种是日渐臃肿,客户忍不了了,痛下决心,宁愿付出一段时间内业务双轨运行,忍受在新、旧系统上重复操作,期间业务发生震荡甚至短暂停顿的代价,也要将整套旧系统彻底淘汰掉,第二种情况笔者亲眼看见过不少。
第三种是日渐臃肿,客户忍不了,系统也很难淘汰。此时迫于外部压力,微服务会作为一种能够将系统部分地拆除、修改、更新、替换的技术方案被严肃地论证,若在重构阶段有足够靠谱的技术人员参与,该大型系统的应用代码和数据库都逐渐分离独立,直至孵化出一个个可替换可重生的微服务,微服务的先驱 Netflix 曾在多次演讲中介绍说自己公司属于第三种的成功案例。 29 | 30 | - …… 31 | 32 | 以上列举的这些内外部原因只是举例,肯定不是全部,促使你的产品最终选择微服务的具体理由可能是多种多样,相信你做出向微服务迈进的决策时,一定经过恰当的权衡,认为收益大于成本。微服务最主要的目的是对系统进行有效的拆分,实现物理层面的隔离,微服务的核心价值就是拆分之后的系统能够让局部的单个服务**有可能**实现敏捷地卸载、部署、开发、升级,局部的持续更迭,是系统整体具备 Phoenix 特性的必要条件。 33 | -------------------------------------------------------------------------------- /methodology/pattern/afk.md: -------------------------------------------------------------------------------- 1 | # 扩展性立方体 2 | 3 | AFK Scalability Cube -------------------------------------------------------------------------------- /methodology/pattern/events/README.md: -------------------------------------------------------------------------------- 1 | # 事件驱动架构 2 | 3 | 事件驱动架构(Event Driven Architecture,EDA)本质上是一种应用、组件间的集成架构模式,事件和传统的消息不同,事件具有Schema,所以可以校验事件的有效性,同时EDA具备QoS保障机制,也能够对事件处理失败进行响应。事件驱动架构不仅用于服务解耦,还可应用于下面的场景中: 4 | 5 | - **增强服务韧性**:由于服务间是异步集成的,也就是下游的任何处理失败甚至宕机都不会被上游感知,自然也就不会对上游带来影响; 6 | - **CQRS**(Command Query Responsibility Segregation):把对服务状态有影响的命令用事件来发起,而对服务状态没有影响的查询才使用同步调用的API接口;结合事件驱动架构中的事件溯源(Event Sourcing)可以用于维护 数据变更的一致性,当需要重新构建服务状态时,把事件驱动架构中的事件重新“播放”一遍即可; 7 | - **数据变化通知**:在服务架构下,往往一个服务中的数据发生变化,另外的服务会感兴趣,比如用户订单 完成后,积分服务、信用服务等都需要得到事件通知并更新用户积分和信用等级; 8 | - **构建开放式接口**:在事件驱动架构下,事件的提供者并不用关心有哪些订阅者,不像服务调用的场景——数据的产生者需要知道数据的消费者在哪里并调用它,因此保持了接口的开放性; 9 | - **事件流处理**:应用于大量事件流(而非离散事件)的数据分析场景,典型应用是基于Kafka的日志处理; 10 | - **基于事件触发的响应**:在IoT时代大量传感器产生的数据,不会像人机交互一样需要等待处理结果的返回,天然适合用事件驱动架构来构建数据处理应用。 -------------------------------------------------------------------------------- /methodology/pattern/events/cep.md: -------------------------------------------------------------------------------- 1 | # 复杂事件处理 -------------------------------------------------------------------------------- /methodology/pattern/events/cqrs.md: -------------------------------------------------------------------------------- 1 | # 查询职责分离 2 | 3 | -------------------------------------------------------------------------------- /methodology/pattern/events/es.md: -------------------------------------------------------------------------------- 1 | # 事件溯源 -------------------------------------------------------------------------------- /methodology/pattern/orchestration.md: -------------------------------------------------------------------------------- 1 | # 编排与协同 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awesome-fenix", 3 | "version": "1.0.0", 4 | "description": "explore architecture", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "dev": "vuepress dev .", 9 | "build": "vuepress build .", 10 | "export": "vuepress export .", 11 | "deoply:refresh-cdn": "node .vuepress/qcloud.cdn.refresh.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/fenixsoft/awesome-fenix.git" 16 | }, 17 | "author": "icyfenix@gmail.com", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/fenixsoft/awesome-fenix/issues" 21 | }, 22 | "homepage": "https://github.com/fenixsoft/awesome-fenix#readme", 23 | "devDependencies": { 24 | "@vuepress/plugin-back-to-top": "^1.4.0", 25 | "@vuepress/plugin-google-analytics": "^1.4.0", 26 | "easy-pdf-merge": "^0.2.0", 27 | "github-buttons": "^2.13.0", 28 | "markdown-it-abbr": "^1.0.4", 29 | "markdown-it-figure": "^0.2.0", 30 | "markdown-it-ins": "^3.0.0", 31 | "markdown-it-mermaid": "^0.2.5", 32 | "markdown-it-smartarrows": "^1.0.1", 33 | "markdown-it-sub": "^1.0.0", 34 | "markdown-it-sup": "^1.0.0", 35 | "moment": "^2.24.0", 36 | "puppeteer": "^1.11.0", 37 | "qcloud-cdn-node-sdk": "^1.0.0", 38 | "vuepress": "^1.4.0", 39 | "vuepress-plugin-comment": "^0.7.3" 40 | }, 41 | "dependencies": { 42 | "markdown-it-fontawesome": "^0.3.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tricks/2020/graalvm/README.md: -------------------------------------------------------------------------------- 1 | # Graal VM 2 | 3 | :::tip 视频公开课 4 | 5 | 本节笔者有公开课介绍:[《GraalVM:云原生时代的 Java》](/tricks/2020/graalvm/video) 6 | 7 | ::: 8 | 9 | 网上每隔一段时间就能见到几条“未来 X 语言将会取代 Java”的新闻,此处“X”可以用 Kotlin、Golang、Dart、JavaScript、Python……等各种编程语言来代入。这大概就是长期占据[编程语言榜单](https://www.tiobe.com/tiobe-index/)第一位的烦恼,天下第一总避免不了挑战者相伴。 10 | 11 | 如果 Java 有拟人化的思维,它应该从来没有惧怕过被哪一门语言所取代,Java“天下第一”的底气不在于语法多么先进好用,而是来自它庞大的用户群和极其成熟的软件生态,这在朝夕之间难以撼动。不过,既然有那么多新、旧编程语言的兴起躁动,说明必然有其需求动力所在,譬如互联网之于 JavaScript、人工智能之于 Python,微服务风潮之于 Golang 等等。大家都清楚不太可能有哪门语言能在每一个领域都尽占优势,Java 已是距离这个目标最接近的选项,但若“天下第一”还要百尺竿头更进一步的话,似乎就只能忘掉 Java 语言本身,踏入无招胜有招的境界。 12 | 13 | 2018 年 4 月,Oracle Labs 新公开了一项黑科技:[Graal VM](https://www.graalvm.org/),从它的口号“Run Programs Faster Anywhere”就能感觉到一颗蓬勃的野心,这句话显然是与 1995 年 Java 刚诞生时的“Write Once,Run Anywhere”在遥相呼应。 14 | 15 | :::center 16 | ![](./images/grallvm.png) 17 | Graal VM 18 | ::: 19 | 20 | Graal VM 被官方称为“Universal VM”和“Polyglot VM”,这是一个在 HotSpot 虚拟机基础上增强而成的跨语言全栈虚拟机,可以作为“任何语言”的运行平台使用,这里“任何语言”包括了 Java、Scala、Groovy、Kotlin 等基于 Java 虚拟机之上的语言,还包括了 C、C++、Rust 等基于 LLVM 的语言,同时支持其他像 JavaScript、Ruby、Python 和 R 语言等等。Graal VM 可以无额外开销地混合使用这些编程语言,支持不同语言中混用对方的接口和对象,也能够支持这些语言使用已经编写好的本地库文件。 21 | 22 | Graal VM 的基本工作原理是将这些语言的源代码(例如 JavaScript)或源代码编译后的中间格式(例如 LLVM 字节码)通过解释器转换为能被 Graal VM 接受的[中间表示](https://zh.wikipedia.org/wiki/%E4%B8%AD%E9%96%93%E8%AA%9E%E8%A8%80)(Intermediate Representation,IR),譬如设计一个解释器专门对 LLVM 输出的字节码进行转换来支持 C 和 C++语言,这个过程称为“[程序特化](https://en.wikipedia.org/wiki/Partial_evaluation)”(Specialized,也常称为 Partial Evaluation)。Graal VM 提供了[Truffle 工具集](https://github.com/oracle/graal/tree/master/truffle)来快速构建面向一种新语言的解释器,并用它构建了一个称为[Sulong](https://github.com/oracle/graal/tree/master/sulong)的高性能 LLVM 字节码解释器。 23 | 24 | 以更严格的角度来看,Graal VM 才是真正意义上与物理计算机相对应的高级语言虚拟机,理由是它与物理硬件的指令集一样,做到了只与机器特性相关而不与某种高级语言特性相关。Oracle Labs 的研究总监 Thomas Wuerthinger 在接受[InfoQ 采访](https://www.infoq.com/news/2018/04/oracle-graalvm-v1/)时谈到:“随着 Graal VM 1.0 的发布,我们已经证明了拥有高性能的多语言虚拟机是可能的,并且实现这个目标的最佳方式不是通过类似 Java 虚拟机和微软 CLR 那样带有语言特性的字节码”。对于一些本来就不以速度见长的语言运行环境,由于 Graal VM 本身能够对输入的中间表示进行自动优化,在运行时还能进行即时编译优化,往往使用 Graal VM 实现能够获得比原生编译器更优秀的执行效率,譬如 Graal.js 要优于 Node.js、Graal.Python 要优于 CPtyhon,TruffleRuby 要优于 Ruby MRI,FastR 要优于 R 语言等等。 25 | 26 | 针对 Java 而言,Graal VM 本来就是在 HotSpot 基础上诞生的,天生就可作为一套完整的符合 Java SE 8 标准的 Java 虚拟机来使用。它和标准的 HotSpot 差异主要在即时编译器上,其执行效率、编译质量目前与标准版的 HotSpot 相比也是互有胜负。但现在 Oracle Labs 和美国大学里面的研究院所做的最新即时编译技术的研究全部都迁移至基于 Graal VM 之上进行了,其发展潜力令人期待。如果 Java 语言或者 HotSpot 虚拟机真的有被取代的一天,那从现在看来 Graal VM 是希望最大的一个候选项,这场革命很可能会在 Java 使用者没有明显感觉的情况下悄然而来,Java 世界所有的软件生态都没有发生丝毫变化,但天下第一的位置已经悄然更迭。 27 | -------------------------------------------------------------------------------- /tricks/2020/graalvm/graal-compiler.md: -------------------------------------------------------------------------------- 1 | # 新一代即时编译器 2 | 3 | 对需要长时间运行的应用来说,由于经过充分预热,热点代码会被 HotSpot 的探测机制准确定位捕获,并将其编译为物理硬件可直接执行的机器码,在这类应用中 Java 的运行效率很大程度上是取决于即时编译器所输出的代码质量。 4 | 5 | HotSpot 虚拟机中包含有两个即时编译器,分别是编译时间较短但输出代码优化程度较低的客户端编译器(简称为 C1)以及编译耗时长但输出代码优化质量也更高的服务端编译器(简称为 C2),通常它们会在分层编译机制下与解释器互相配合来共同构成 HotSpot 虚拟机的执行子系统的。 6 | 7 | 自 JDK 10 起,HotSpot 中又加入了一个全新的即时编译器:Graal 编译器,看名字就可以联想到它是来自于前一节提到的 Graal VM。Graal 编译器是作为 C2 编译器替代者的角色登场的。C2 的历史已经非常长了,可以追溯到 Cliff Click 大神读博士期间的作品,这个由 C++写成的编译器尽管目前依然效果拔群,但已经复杂到连 Cliff Click 本人都不愿意继续维护的程度。而 Graal 编译器本身就是由 Java 语言写成,实现时又刻意与 C2 采用了同一种名为“Sea-of-Nodes”的高级中间表示(High IR)形式,使其能够更容易借鉴 C2 的优点。Graal 编译器比 C2 编译器晚了足足二十年面世,有着极其充沛的后发优势,在保持能输出相近质量的编译代码的同时,开发效率和扩展性上都要显著优于 C2 编译器,这决定了 C2 编译器中优秀的代码优化技术可以轻易地移植到 Graal 编译器上,但是反过来 Graal 编译器中行之有效的优化在 C2 编译器里实现起来则异常艰难。这种情况下,Graal 的编译效果短短几年间迅速追平了 C2,甚至某些测试项中开始逐渐反超 C2 编译器。Graal 能够做比 C2 更加复杂的优化,如“[部分逃逸分析](http://www.ssw.uni-linz.ac.at/Research/Papers/Stadler14/Stadler2014-CGO-PEA.pdf)”(Partial Escape Analysis),也拥有比 C2 更容易使用“[激进预测性优化](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.78.6063)”(Aggressive Speculative Optimization)的策略,支持自定义的预测性假设等等。 8 | 9 | 今天的 Graal 编译器尚且年幼,还未经过足够多的实践验证,所以仍然带着“实验状态”的标签,需要用开关参数去激活,这让笔者不禁联想起 JDK 1.3 时代,HotSpot 虚拟机刚刚横空出世时的场景,同样也是需要用开关激活,也是作为 Classic 虚拟机的替代品的一段历史。 10 | 11 | Graal 编译器未来的前途可期,作为 Java 虚拟机执行代码的最新引擎,它的持续改进,会同时为 HotSpot 与 Graal VM 注入更快更强的驱动力。 12 | -------------------------------------------------------------------------------- /tricks/2020/graalvm/graalvm-native.md: -------------------------------------------------------------------------------- 1 | # 没有虚拟机的 Java 2 | 3 | 尽管 Java 已经看清楚了在微服务时代的前进目标,但是,Java 语言和生态在微服务、微应用环境中的天生的劣势并不会一蹴而就地被解决,通往这个目标的道路注定会充满荆棘;尽管已经有了放弃“一次编写,到处运行”、放弃语言动态性的思想准备,但是,这些特性并不单纯是宣传口号,它们在 Java 语言诞生之初就被植入到基因之中,当 Graal VM 试图打破这些规则的同时,也受到了 Java 语言和在其之上的生态的强烈反噬,笔者选择其中最主要的一些困难列举如下: 4 | 5 | - 某些 Java 语言的特性,使得 Graal VM 编译本地镜像的过程变得极为艰难。譬如常见的反射,除非使用[安全管理器](/architect-perspective/general-architecture/system-security/authentication.html)去专门进行认证许可,否则反射机制具有在运行期动态调用几乎所有 API 接口的能力,且具体会调用哪些接口,在程序不会真正运行起来的编译期是无法获知的。反射显然是 Java 不能放弃不能妥协的重要特性,为此,只能由程序的开发者明确地告知 Graal VM 有哪些代码可能被反射调用(通过 JSON 配置文件的形式),Graal VM 才能在编译本地程序时将它们囊括进来。 6 | 7 | ```json 8 | [ 9 | { 10 | name: "com.github.fenixsoft.SomeClass", 11 | allDeclaredConstructors: true, 12 | allPublicMethods: true 13 | }, 14 | { 15 | name: "com.github.fenixsoft.AnotherClass", 16 | fileds: [{name: "foo"}, {name: "bar"}], 17 | methods: [{ 18 | name: "", 19 | parameterTypes: ["char[]"] 20 | }] 21 | }, 22 | // something else …… 23 | ] 24 | ``` 25 | 26 | 这是一种可操作性极其低下却又无可奈何的解决方案,即使开发者接受不厌其烦地列举出自己代码中所用到的反射 API,但他们又如何能保证程序所引用的其他类库的反射行为都已全部被获知,其中没有任何遗漏?与此类似的还有另外一些语言特性,如动态代理等。另外,一切非代码性质的资源,如最典型的配置文件等,也都必须明确加入配置中才能被 Graal VM 编译打包。这导致了如果没有专门的工具去协助,使用 Graal VM 编译 Java 的遗留系统即使理论可行,实际操作也将是极度的繁琐。 27 | 28 | - 大多数运行期对字节码的生成和修改操作,在 Graal VM 看来都是无法接受的,因为 Substrate VM 里面不再包含即时编译器和字节码执行引擎,所以一切可能被运行的字节码,都必须经过 AOT 编译成为原生代码。请不要觉得运行期直接生成字节码会很罕见,误以为导致的影响应该不算很大。事实上,多数实际用于生产的 Java 系统都或直接或间接、或多或少引用了 ASM、CGLIB、Javassist 这类字节码库。举个例子,CGLIB 是通过运行时产生字节码(生成代理类的子类)来做动态代理的,长期以来这都是 Java 世界里进行类增强的主流形式,因为面向接口的增强可以使用 JDK 自带的动态代理,但对类的增强则并没有多少选择的余地。CGLIB 也是 Spring 用来做类增强的选择,但 Graal VM 明确表示是不可能支持 CGLIB 的,因此,这点就必须由用户(面向接口编程)、框架(Spring 这些 DI 框架放弃 CGLIB 增强)和 Graal VM(起码得支持 JDK 的动态代理,留条活路可走)来共同解决。自 Spring Framework 5.2 起,@Configuration 注解中加入了一个新的 proxyBeanMethods 参数,设置为 false 则可避免 Spring 对于非接口类型的 Bean 进行代理。同样地,对应在 Spring Boot 2.2 中,@SpringBootApplication 注解也增加了 proxyBeanMethods 参数,通常采用 Graal VM 去构建的 Spring Boot 本地应用都需要设置该参数。 29 | 30 | - 一切 HotSpot 虚拟机本身的内部接口,譬如 JVMTI、JVMCI 等,都将不复存在了——在本地镜像中,连 HotSpot 本身都被消灭了,这些接口自然成了无根之木。这对使用者一侧的最大影响是再也无法进行 Java 语言层次的远程调试了,最多只能进行汇编层次的调试。在生产系统中一般也没有人这样做,开发环境就没必要采用 Graal VM 编译,这点的实际影响并不算大。 31 | 32 | - Graal VM 放弃了一部分可以妥协的语言和平台层面的特性,譬如 Finalizer、安全管理器、InvokeDynamic 指令和 MethodHandles,等等,在 Graal VM 中都被声明为不支持的,这些妥协的内容大多倒并非全然无法解决,主要是基于工作量性价比的原因。能够被放弃的语言特性,说明确实是影响范围非常小的,所以这个对使用者来说一般是可以接受的。 33 | 34 | - …… 35 | 36 | 以上,是 Graal VM 在 Java 语言中面临的部分困难,在整个 Java 的生态系统中,数量庞大的第三方库才是真正最棘手的难题。可以预料,这些第三方库一旦脱离了 Java 虚拟机,在原生环境中肯定会暴露出无数千奇百怪的异常行为。Graal VM 团队对此的态度非常务实,并没有直接硬啃。要建设可持续、可维护的 Graal VM,就不能为了兼容现有 JVM 生态,做出过多的会影响性能、优化空间和未来拓展的妥协牺牲,为此,应该也只能反过来由 Java 生态去适应 Graal VM,这是 Graal VM 团队明确传递出对第三方库的态度: 37 | 38 | :::quote 3rd party libraries 39 | 40 | Graal VM native support needs to be sustainable and maintainable, that's why we do not want to maintain fragile pathches for the whole JVM ecosystem. 41 | 42 | The ecosystem of libraries needs to support it natively. 43 | 44 | ::: right 45 | 46 | —— Sébastien Deleuze,[DEVOXX 2019](https://www.youtube.com/watch?v=3eoAxphAUIg) 47 | 48 | ::: 49 | 50 | 为了推进 Java 生态向 Graal VM 兼容,Graal VM 主动拉拢了 Java 生态中最庞大的一个派系:Spring。从 2018 年起,来自 Oracle 的 Graal VM 团队与来自 Pivotal 的 Spring 团队已经紧密合作了很长的一段时间,共同创建了[Spring Graal Native](https://github.com/spring-projects-experimental/spring-graal-native)项目来解决 Spring 全家桶在 Graal VM 上的运行适配问题,在不久的将来(预计应该是 2020 年 10 月左右),下一个大的 Spring 版本(Spring Framework 5.3、Spring Boot 2.3)的其中一项主要改进就是能够开箱即用地支持 Graal VM,这样,用于微服务环境的 Spring Cloud 便会获得不受 Java 虚拟机束缚的更广阔的舞台空间。 51 | -------------------------------------------------------------------------------- /tricks/2020/graalvm/images/graal-hotspot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2020/graalvm/images/graal-hotspot.png -------------------------------------------------------------------------------- /tricks/2020/graalvm/images/grallvm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2020/graalvm/images/grallvm.png -------------------------------------------------------------------------------- /tricks/2020/graalvm/images/substrate1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2020/graalvm/images/substrate1.png -------------------------------------------------------------------------------- /tricks/2020/graalvm/images/substrate2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2020/graalvm/images/substrate2.png -------------------------------------------------------------------------------- /tricks/2020/graalvm/substratevm.md: -------------------------------------------------------------------------------- 1 | # 向原生迈进 2 | 3 | 对不需要长时间运行的,或者小型化的应用而言,Java(而不是指 Java ME)天生就带有一些劣势,这里并不光是指跑个 HelloWorld 也需要百多兆的 JRE 之类的问题,而更重要的是指近几年从大型单体应用架构向小型微服务应用架构发展的技术潮流下,Java 表现出来的不适应。 4 | 5 | 在微服务架构的视角下,应用拆分后,单个微服务很可能就不再需要再面对数十、数百 GB 乃至 TB 的内存,有了高可用的服务集群,也无须追求单个服务要 7×24 小时不可间断地运行,它们随时可以中断和更新;但相应地,Java 的启动时间相对较长、需要预热才能达到最高性能等特点就显得相悖于这样的应用场景。在无服务架构中,矛盾则可能会更加突出,比起服务,一个函数的规模通常会更小,执行时间会更短,当前最热门的无服务运行环境 AWS Lambda 所允许的最长运行时间仅有 15 分钟。 6 | 7 | 一直把软件服务作为重点领域的 Java 自然不可能对此视而不见,在最新的几个 JDK 版本的功能清单中,已经陆续推出了跨进程的、可以面向用户程序的类型信息共享(Application Class Data Sharing,AppCDS,允许把加载解析后的类型信息缓存起来,从而提升下次启动速度,原本 CDS 只支持 Java 标准库,在 JDK 10 时的 AppCDS 开始支持用户的程序代码)、无操作的垃圾收集器(Epsilon,只做内存分配而不做回收的收集器,对于运行完就退出的应用十分合适)等改善措施。而酝酿中的一个更彻底的解决方案,是逐步开始对提前编译(Ahead of Time Compilation,AOT)提供支持。 8 | 9 | 提前编译是相对于即时编译的概念,提前编译能带来的最大好处是 Java 虚拟机加载这些已经预编译成二进制的库之后就能够直接调用,而无须再等待即时编译器在运行时将其编译成二进制机器码。理论上,提前编译可以减少即时编译带来的预热时间,减少 Java 应用长期给人带来的“第一次运行慢”不良体验,可以放心地进行很多全程序的分析行为,可以使用时间压力更大的优化措施。 10 | 11 | 但是提前编译的坏处也很明显,它破坏了 Java“一次编写,到处运行”的承诺,必须为每个不同的硬件、操作系统去编译对应的发行包。也显著降低了 Java 链接过程的动态性,必须要求加载的代码在编译期就是全部已知的,而不能再是运行期才确定,否则就只能舍弃掉已经提前编译好的版本,退回到原来的即时编译执行状态。 12 | 13 | 早在 JDK 9 时期,Java 就提供了实验性的 Jaotc 命令来进行提前编译,不过多数人试用过后都颇感失望,大家原本期望的是类似于 Excelsior JET 那样的编译过后能生成本地代码完全脱离 Java 虚拟机运行的解决方案,但 Jaotc 其实仅仅是代替掉即时编译的一部分作用而已,仍需要运行于 HotSpot 之上。 14 | 15 | 直到[Substrate VM](https://github.com/oracle/graal/tree/master/substratevm)出现,才算是满足了人们心中对 Java 提前编译的全部期待。Substrate VM 是在 Graal VM 0.20 版本里新出现的一个极小型的运行时环境,包括了独立的异常处理、同步调度、线程管理、内存管理(垃圾收集)和 JNI 访问等组件,目标是代替 HotSpot 用来支持提前编译后的程序执行。它还包含了一个本地镜像的构造器(Native Image Generator)用于为用户程序建立基于 Substrate VM 的本地运行时镜像。这个构造器采用指针分析(Points-To Analysis)技术,从用户提供的程序入口出发,搜索所有可达的代码。在搜索的同时,它还将执行初始化代码,并在最终生成可执行文件时,将已初始化的堆保存至一个堆快照之中。这样一来,Substrate VM 就可以直接从目标程序开始运行,而无须重复进行 Java 虚拟机的初始化过程。但相应地,原理上也决定了 Substrate VM 必须要求目标程序是完全封闭的,即不能动态加载其他编译期不可知的代码和类库。基于这个假设,Substrate VM 才能探索整个编译空间,并通过静态分析推算出所有虚方法调用的目标方法。 16 | 17 | Substrate VM 带来的好处是能显著降低内存占用及启动时间,由于 HotSpot 本身就会有一定的内存消耗(通常约几十 MB),这对最低也从几 GB 内存起步的大型单体应用来说并不算什么,但在微服务下就是一笔不可忽视的成本。根据 Oracle 官方给出的[测试数据](https://www.infoq.com/presentations/graalvm-performance/),运行在 Substrate VM 上的小规模应用,其内存占用和启动时间与运行在 HotSpot 相比有了 5 倍到 50 倍的下降,具体结果如下图所示: 18 | 19 | :::center 20 | ![](./images/substrate1.png) 21 | 内存占用对比 22 | ![](./images/substrate2.png) 23 | 启动时间对比 24 | ::: 25 | 26 | Substrate VM 补全了 Graal VM“Run Programs Faster Anywhere”愿景蓝图里最后的一块拼图,让 Graal VM 支持其他语言时不会有重量级的运行负担。譬如运行 JavaScript 代码,Node.js 的 V8 引擎执行效率非常高,但即使是最简单的 HelloWorld,它也要使用约 20MB 的内存,而运行在 Substrate VM 上的 Graal.js,跑一个 HelloWorld 则只需要 4.2MB 内存而已,且运行速度与 V8 持平。Substrate VM 的轻量特性,使得它十分适合于嵌入至其他系统之中,譬如[Oracle 自家的数据库](https://oracle.github.io/oracle-db-mle)就已经开始使用这种方式支持用不同的语言代替 PL/SQL 来编写存储过程。 27 | -------------------------------------------------------------------------------- /tricks/2020/graalvm/video.md: -------------------------------------------------------------------------------- 1 | # 视频讲解 2 | 3 |
4 | 5 |
6 | 7 | -------------------------------------------------------------------------------- /tricks/2020/java-crisis/images/future.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2020/java-crisis/images/future.png -------------------------------------------------------------------------------- /tricks/2020/java-crisis/images/inline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2020/java-crisis/images/inline.png -------------------------------------------------------------------------------- /tricks/2020/java-crisis/images/matrices.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2020/java-crisis/images/matrices.png -------------------------------------------------------------------------------- /tricks/2020/java-crisis/images/startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2020/java-crisis/images/startup.png -------------------------------------------------------------------------------- /tricks/2020/java-crisis/images/threads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2020/java-crisis/images/threads.png -------------------------------------------------------------------------------- /tricks/2021/README.md: -------------------------------------------------------------------------------- 1 | # 2021年文章集合 2 | 3 | - [JDKBuild Env for Project Portoal](./jdkbuild) 4 | - [Into Kubernetes](./k3s) 5 | -------------------------------------------------------------------------------- /tricks/2021/fenix-cli/assets/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/fenix-cli/assets/1.gif -------------------------------------------------------------------------------- /tricks/2021/fenix-cli/assets/10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/fenix-cli/assets/10.gif -------------------------------------------------------------------------------- /tricks/2021/fenix-cli/assets/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/fenix-cli/assets/3.gif -------------------------------------------------------------------------------- /tricks/2021/fenix-cli/assets/4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/fenix-cli/assets/4.gif -------------------------------------------------------------------------------- /tricks/2021/fenix-cli/assets/5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/fenix-cli/assets/5.gif -------------------------------------------------------------------------------- /tricks/2021/fenix-cli/assets/7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/fenix-cli/assets/7.gif -------------------------------------------------------------------------------- /tricks/2021/fenix-cli/assets/8.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/fenix-cli/assets/8.gif -------------------------------------------------------------------------------- /tricks/2021/fenix-cli/assets/9.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/fenix-cli/assets/9.gif -------------------------------------------------------------------------------- /tricks/2021/jdkbuild/README.md: -------------------------------------------------------------------------------- 1 | # 在容器中编译OpenJDK 2 | 3 | 4 | 5 | ## 编译环境 6 | 7 | 8 | 9 | ## 按需编译 10 | 11 | 12 | 13 | 14 | 15 | ## 裁剪 16 | 17 | 18 | 19 | 20 | 21 | ## Alpine Musl 22 | 23 | ``` 24 | sh ./configure --with-jvm-variants=server \ 25 | --with-boot-jdk=$BOOT_JDK \ 26 | --with-build-jdk=$BUILD_JDK \ 27 | --openjdk-target=x86_64-unknown-linux-musl \ 28 | --with-devkit=$DEVKIT \ 29 | --with-sysroot=$SYSROOT 30 | ``` -------------------------------------------------------------------------------- /tricks/2021/k3s/README.md: -------------------------------------------------------------------------------- 1 | # Into Kubernetes 2 | 3 | ## Content 4 | 5 | - [编译K3s](./build.md) 6 | - 组件研究 7 | - [kube-apiserver](./apiserver) 8 | - node-agent 9 | - kubectl 10 | 11 | ## K3s 12 | 13 | K3S是一个由Rancher维护的轻量级Kubernetes发行版,K3S并不是什么单词的缩写,它的意思是K与S之间有3个字母,共计5个字母,意味与10个字母长的K8S的一半。K3S主要用于小规模集群环境,如开发、持续集成、物联网、边缘计算等领域。特点是轻量、便捷、小型化,具体体现为: 14 | 15 | 1. 整个产品的所有组件,均以一个单独二进制程序包的形式存在,而且容量不到100 MB(1.20版实际为53 MB)。与原版Kubernetes将系统拆分为api-server、manager等多个Static Pod相比,部署、启动、重启、调试都相当便捷。这点也是推荐使用K3S来作为Kubernetes源码研究入门的重要考量因素。 16 | 2. 增加了SQLIte3的存储支持,并默认以SQLIte3代替Etcd作为控制平面的数据存储。K3S最初的目标并不支持高可用集群(控制平面以多个Master节点构成,现在K3S已经支持高可用部署了),对于单节点来说Etcd并没有价值,使用SQLIte3进一步降低了Kubernetes的技术栈门槛。 17 | 3. 最小化的操作系统和网络依赖,操作系统方面,只要求完整的Linux Kernel和cgroups支持,对具体发行版和其他第三方前置依赖没有更多的要求。网络方面,内置了应用最多的Flannel VXLAN作为默认的CNI网络,且简化了诸多网络操作,如worker无需专门想Kubernetes控制平面通过WebSocket Tunnel暴露kubelet API。 18 | 4. 内置常用组件,K3S在裁剪K3S非默认功能的同时,并非一味追求最小提及,它将易用性摆在小型化更高的优先级上,内置了[Flannel VXLAN](https://github.com/coreos/flannel) 、[CoreDNS](https://coredns.io/)、[Traefik](https://containo.us/traefik/)、[Klipper-lb](https://github.com/rancher/klipper-lb)、[Helm-controller](https://github.com/k3s-io/helm-controller)等一系列常用的组件,甚至内置了一批用于提升运维便捷性的常用主机工具:[Host utilities](https://github.com/k3s-io/k3s-root)(iptables/nftables, ebtables, ethtool, socat……) 19 | -------------------------------------------------------------------------------- /tricks/2021/k3s/apiserver/README.md: -------------------------------------------------------------------------------- 1 | # kube-apiserver源码解读 2 | 3 | [官方介绍](https://kubernetes.io/zh/docs/reference/command-line-tools-reference/kube-apiserver/):Kubernetes API 服务器验证并配置 API 对象的数据, 这些对象包括 pods、services、replicationcontrollers 等。 API 服务器为 REST 操作提供服务,并为集群的共享状态提供前端, 所有其他组件都通过该前端进行交互。 4 | 5 | ## 解析: 6 | 7 | kube-apiserver在Kubernetes Control Panel中的作用是管理资源,管理的含义是开放出一系列的[Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/),提供对资源的操作提供认证、授权、CRUD等接口,供Agent和其他组件使用。可以认为是各个模块的数据交互和通信的枢纽。 8 | 9 | 这些接口的变动最终都会存储到后端的 Etcd 中(K3S中默认是被Kind代理的SQLIte),kube-apiserver是Kubernetes中直接与Etcd打交道的组件。 10 | 11 | 在kube-apiserver中,API资源一共有四类,分别是: 12 | 13 | - 核心资源组,路径为:`https://master-ip:6443/api/v1/namespaces/$ns/$resource-name`,如nodes、pods、services等 14 | - 具名资源组(Named Group),路径为:`https://master-ip:6443/apis/$group-name/$version/namespaces/$ns/$resource-name`,譬如`/apis/batch/v1/jobs`、`/apis/extendsions/v2alpha1/namespaces/`。具体的API Groups,可以在对应版本的Kubernetes的[API References](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#-strong-api-groups-strong-)页面中查询到。 15 | - 有外部添加的CR[D]资源。 16 | - 用于暴露系统状态的一些固定端点,譬如`/metrics`(度量数据)、`/healthz`(健康数据)、`/ui`(Dashboard)、`/logs`(日志)、`/swaggerapi`(OpenAPI)等 17 | 18 | 这四类API资源,是以委托模式,通过DelegationTarget接口,由API Extensions Server、Kube API Server、Aggregator Server三个服务器所提供,它们的作用是: 19 | 20 | 1. **API Extensions Server**包含了对用户自定义扩展资源(CRD/CR)的支持,譬如增加一个CRD,它用到的API Version、Group、Handler、Hook等功能,涉及到一系列的Controller,如Discovery、Naming、NonStructruraSchema、ApiApproval、Finalizing等等。每一个Controller都会在一条goroutine中执行它的Run()方法,具体Controller如下: 21 | 1. `openapiController`:将CRD资源的变化同步至提供的OpenAPI文档,可通过访问 `/openapi/v2`进行查看; 22 | 2. `crdController`:负责将CRD信息注册到apiVersions和apiResources中,两者的信息可通过`$ kubectl api-versions`和`$ kubectl api-resources`查看; 23 | 3. `namingController`:检查CRD对象是否有命名冲突,可在`crd.status.conditions`中查看; 24 | 4. `establishingController`:检查CRD是否处于正常状态,可在`crd.status.conditions`中查看; 25 | 5. `nonStructuralSchemaController`:检查CRD对象结构是否正常,可在`crd.status.conditions`中查看; 26 | 6. `apiApprovalController`:检查CRD是否遵循Kubernetes API声明策略,可在`crd.status.conditions`中查看; 27 | 7. `finalizingController`:类似于finalizes的功能,与CR的删除有关; 28 | 2. **Kube API Server**包含了对传统核心资源(Legacy API,就是以`/api`开头的资源),譬如Pod、Event、LimitRange、ResourceQuote等等,以及扩展资源(就是以`/apis`开头的资源,如`/apis/batch`、`/aps/extensions`),譬如Jobs、CronJobs、Namspaces的支持,这些都是在Kubernetes中以编码固定(不像CRD那样通过外部配置扩展)的资源,如何与APIServer交互都是由固定的内部函数来完成的。此外,Kube API Server还负责对请求的通用前置处理,如认证、授权等。 29 | 1. `GenericAPIServer.NewDefaultAPIGroupInfo()`中读取默认的API资源信息。 30 | 2. `GenericAPIServer.InstallAPIGroups()`中注册API资源,即将根据其定义,生成REST Endpoint供外部访问,并加入到DiscoveryManager中,供外部访问。 31 | 3. **Aggregator Server**聚合了API Extensions Server的自定义资源和Kube API Server的核心资源,功能类似于一个七层负载均衡,将来自用户的请求拦截转发给其他服务器,并且负责整个APIServer的Discovery功能,与API Extensions Server类似,Aggregator Server中也包含了一系列的Controller,其作用如下: 32 | 1. `apiserviceRegistrationController`:负责APIServices中资源的注册与删除; 33 | 2. `availableConditionController`:维护APIServices的可用状态,包括其引用Service是否可用等; 34 | 3. `autoRegistrationController`:用于保持API中存在的一组特定的APIServices; 35 | 4. `crdRegistrationController`:负责将CRD GroupVersions自动注册到APIServices中; 36 | 5. `openAPIAggregationController`:将APIServices资源的变化同步至提供的OpenAPI文档; 37 | 38 | ## 源码: 39 | 40 | - [K3S启动APIServer的环境准备](env.md) 41 | - [K8S-APIServer启动过程](bootstrap.md) 42 | 43 | -------------------------------------------------------------------------------- /tricks/2021/k3s/apiserver/bootstrap.md: -------------------------------------------------------------------------------- 1 | # K8S-APIServer启动过程 2 | 3 | 启动入口:`k8s.io/kubernetes/cmd/kube-apiserver/app/server.go::Run()`,内部调用了两个方法: 4 | 1. `CreateServerChain()`,名字中 “Server Chain”包含了API Extensions Server、Kube API Server、Aggregator Server三个服务器的资源配置及路由创建。 5 | 6 | 1. **API Extensions Server**包含了对用户自定义扩展资源(CRD/CR)的支持,譬如增加一个CRD,它用到的API Version、Group、Handler、Hook等功能,涉及到一系列的Controller,如Discovery、Naming、NonStructruraSchema、ApiApproval、Finalizing等等。每一个Controller都会在一条goroutine中执行它的Run()方法,具体Controller如下: 7 | 1. `openapiController`:将CRD资源的变化同步至提供的OpenAPI文档,可通过访问 `/openapi/v2`进行查看; 8 | 2. `crdController`:负责将CRD信息注册到apiVersions和apiResources中,两者的信息可通过`$ kubectl api-versions`和`$ kubectl api-resources`查看; 9 | 3. `namingController`:检查CRD对象是否有命名冲突,可在`crd.status.conditions`中查看; 10 | 4. `establishingController`:检查CRD是否处于正常状态,可在`crd.status.conditions`中查看; 11 | 5. `nonStructuralSchemaController`:检查CRD对象结构是否正常,可在`crd.status.conditions`中查看; 12 | 6. `apiApprovalController`:检查CRD是否遵循Kubernetes API声明策略,可在`crd.status.conditions`中查看; 13 | 7. `finalizingController`:类似于finalizes的功能,与CR的删除有关; 14 | 2. **Kube API Server**包含了对传统核心资源(Legacy API,就是以`/api`开头的资源),譬如Pod、Event、LimitRange、ResourceQuote等等,以及扩展资源(就是以`/apis`开头的资源,如`/apis/batch`、`/aps/extensions`),譬如Jobs、CronJobs、Namspaces的支持,这些都是在Kubernetes中以编码固定(不像CRD那样通过外部配置扩展)的资源,如何与APIServer交互都是由固定的内部函数来完成的。此外,Kube API Server还负责对请求的通用前置处理,如认证、授权等。 15 | 1. `GenericAPIServer.NewDefaultAPIGroupInfo()`中读取默认的API资源信息。 16 | 2. `GenericAPIServer.InstallAPIGroups()`中注册API资源,即将根据其定义,生成REST Endpoint供外部访问,并加入到DiscoveryManager中,供外部访问。 17 | 3. **Aggregator Server**聚合了API Extensions Server的自定义资源和Kube API Server的核心资源,功能类似于一个七层负载均衡,将来自用户的请求拦截转发给其他服务器,并且负责整个APIServer的Discovery功能,与API Extensions Server类似,Aggregator Server中也包含了一系列的Controller,其作用如下: 18 | 1. `apiserviceRegistrationController`:负责APIServices中资源的注册与删除; 19 | 2. `availableConditionController`:维护APIServices的可用状态,包括其引用Service是否可用等; 20 | 3. `autoRegistrationController`:用于保持API中存在的一组特定的APIServices; 21 | 4. `crdRegistrationController`:负责将CRD GroupVersions自动注册到APIServices中; 22 | 5. `openAPIAggregationController`:将APIServices资源的变化同步至提供的OpenAPI文档; 23 | 24 | 2. 在API Extensions Server、Kube API Server、Aggregator Server三个服务器完成Controller注册和启动之后,会调用`APIAggregator.PrepareRun()`,启动整个delegation链的运作,这步主要动作是调用`delegationTarget.PrepareRun()`和注册好Healths\Lives\Readys三个探针Endpoint 25 | 26 | 3. 执行`preparedAPIAggregator.Run()`,启动一个HTTP服务器,对外提供API访问服务。 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tricks/2021/k3s/build.md: -------------------------------------------------------------------------------- 1 | # 运行、编译与调试环境 2 | 3 | ## 运行 4 | 5 | 由于K3S是单文件发行的,直接在发布页(https://github.com/k3s-io/k3s/releases)中下载,放到PATH路径上即可运行。 6 | 7 | 源码也可以从发布页中下载source.zip,以保证相同版本,也可以直接在GitHub仓库中拉取(https://github.com/k3s-io/k3s/) 8 | 9 | ## 编译 10 | 11 | K3S是依靠Rancher自家的Dapper来编译,Dapper的作用是在Docker镜像中完成程序构建,以保证在不同机器上都能得到一个稳定的编译环境,屏蔽机器环境对编译的影响,也减轻了准备编译环境的工作负担。 12 | 13 | 我们重新编译K3S是为了得到一个禁用优化、带有调试符号信息的版本,以便下一步调试运行使用。根据国内的实际情况,有以下几个可选项建议改动: 14 | 15 | 1. K3S是直接使用golang-alpine的基础镜像的,然后再使用Docker-in-Docker来运行CI环境,由于Dapper文件经常要被反复运行,所以建议自己构建一个基础镜像,代替golang-alpine,节省每次运行时apk安装环境的时间。 16 | 17 | ``` 18 | FROM golang:1.15.5-alpine3.12 19 | 20 | RUN apk -U --no-cache add bash git gcc musl-dev docker vim less file curl wget ca-certificates jq linux-headers zlib-dev tar zip squashfs-tools npm coreutils \ 21 | python2 openssl-dev libffi-dev libseccomp libseccomp-dev make libuv-static sqlite-dev sqlite-static libselinux libselinux-dev zlib-dev zlib-static 22 | ``` 23 | 24 | 2. 编译文件中写死了给链接器参数加`-w -s`参数删除调试的符号表信息,这个是Kubernetes原版就有的传统了。手动在编译文件中删除掉,并加入编译器参数,禁用优化和方法内联: 25 | 26 | ``` 27 | # 编译器参数:-N是禁用优化,-l是禁止方法内联 28 | gcflags="all=-N -l" 29 | 30 | # 链接器参数:将原来的-w -s删除掉 31 | # 注意链接器参数除了在scrips/build文件外,在打all-in-one package的script/build中也存在 32 | ldflags="" 33 | ``` 34 | 35 | 3. 编译的最后一步是调用binary_size_check.sh,检查编译结果的大小,1.20版的最大容量为61 MB,但加入调试的符号信息之后,会膨胀至83 MB左右,所以这里要改一下以下常量: 36 | 37 | ``` 38 | #原本是MAX_BINARY_SIZE=61000000,这里改到了100 MB 39 | MAX_BINARY_SIZE=100000000 40 | ``` 41 | 42 | 4. 由于我们修改过编译文件,所以要将文件校验开关给关闭掉,在Dapper文件中加入环境变量: 43 | 44 | ``` 45 | ENV SKIP_VALIDATE=true 46 | ``` 47 | 48 | 49 | 5. 最后,如果访问外网有困难,请在Dapper中加上代理: 50 | 51 | ``` 52 | ARG http_proxy=socks5://192.168.31.125:2012 53 | ARG https_proxy=socks5://192.168.31.125:2012 54 | ``` 55 | 56 | 然后直接make一下,就可以在dist/artifacts目录中找到k3s了 57 | 58 | ``` 59 | make 60 | ``` 61 | 62 | ## 调试 63 | 64 | k3s的All-In-One包的入口是`cmd/k3s/main.go`,这里的作用就是自动在一个临时目录(默认为`/var/lib/rancher/k3s`)中,解压出所有运行所需的命令,然后通过`stageAndRun()`方法在子进程中调用。 -------------------------------------------------------------------------------- /tricks/2021/k3s/kubectl/README.md: -------------------------------------------------------------------------------- 1 | # Kubectl源码解读 2 | 3 | 默认的配置文件:`/etc/rancher/k3s/k3s.yaml` -------------------------------------------------------------------------------- /tricks/2021/k3s/kubelet/bootstrap.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/k3s/kubelet/bootstrap.md -------------------------------------------------------------------------------- /tricks/2021/openjdk-for-dummies/images/build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/openjdk-for-dummies/images/build.png -------------------------------------------------------------------------------- /tricks/2021/openjdk-for-dummies/images/debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/openjdk-for-dummies/images/debug.png -------------------------------------------------------------------------------- /tricks/2021/openjdk-for-dummies/images/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/openjdk-for-dummies/images/index.png -------------------------------------------------------------------------------- /tricks/2021/openjdk-for-dummies/images/reg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenixsoft/awesome-fenix/129d1f98a845b338931a14d5c8ea6754ed921440/tricks/2021/openjdk-for-dummies/images/reg.png --------------------------------------------------------------------------------