作者:微信小助手
<section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="padding-top: 10px;padding-right: 10px;padding-left: 10px;box-sizing: border-box;background-color: rgb(239, 239, 239);"> <span style="display: inline-block;width: 5%;line-height: 0.8;font-weight: bolder;font-size: 48px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> “ </section></span> <section style="display: inline-block;vertical-align: top;float: right;width: 90%;line-height: 1.5;font-size: 15px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;">Kubernetes 是这两年最热门、最被人熟知的技术了,它为软件工程师提供了强大的容器编排能力,模糊了开发和运维之间的边界,让我们开发、管理和维护一个大型的分布式系统和项目变得更加容易。</span></p> </section> <section style="clear: both;box-sizing: border-box;"></section> </section> </section> </section> </section> <p style="line-height: 1.75em;"><br></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.5354107648725213" data-s="300,640" src="/upload/1e6a5dfe7230915cf12274940c8f7aa6.png" data-type="png" data-w="706" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">本文会先简单介绍 Kuberentes 的背景、依赖的技术,它的架构以及设计理念,最后会提及一些关键概念和实现原理。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">Kuberentes 的背景</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">作为一个目前在生产环境已经广泛使用的开源项目,Kubernetes 被定义成一个用于自动化部署、扩容和管理容器应用的开源系统;它将一个分布式软件的一组容器打包成一个个更容易管理和发现的逻辑单元。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kubernetes 是希腊语『舵手』的意思,它最开始由 Google 的几位软件工程师创立,深受公司内部 Borg 和 Omega 项目的影响,很多设计都是从 Borg 中借鉴的,同时也对 Borg 的缺陷进行了改进。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kubernetes 目前是 Cloud Native Computing Foundation (CNCF) 的项目,并且是很多公司管理分布式系统的解决方案。</span></p> <figure> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.5618729096989966" data-s="300,640" src="/upload/223fed66856cc6a9be064f1f61a58d50.png" data-type="png" data-w="299" style=""></p> </figure> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在 Kubernetes 统治了容器编排这一领域之前,其实也有很多容器编排方案,例如 compose 和 Swarm,但是在运维大规模、复杂的集群时,这些方案基本已经都被 Kubernetes 替代了。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kubernetes 将已经打包好的应用镜像进行编排,所以如果没有容器技术的发展和微服务架构中复杂的应用关系,其实也很难找到合适的应用场景去使用。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(89, 89, 89);">所以在这里我们会简单介绍 Kubernetes 的两大『依赖』——容器技术和微服务架构。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span></strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>容器技术</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Docker 已经是容器技术的事实标准了,作者在前面的文章中 Docker 核心技术与实现原理 曾经介绍过 Docker 的实现主要依赖于 Linux 的 namespace、cgroups 和 UnionFS。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.3387878787878788" data-s="300,640" src="/upload/285d5c36ce9ab58b71ba99226a9dd5d0.png" data-type="png" data-w="1650" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">它让开发者将自己的应用以及依赖打包到一个可移植的容器中,让应用程序的运行可以实现与环境无关。</span><br></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">我们能够通过 Docker 实现进程、网络以及挂载点和文件系统隔离的环境,并且能够对宿主机的资源进行分配。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这能够让我们在同一个机器上运行多个不同的 Docker 容器,任意一个 Docker 的进程都不需要关心宿主机的依赖,都各自在镜像构建时完成依赖的安装和编译等工作。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这也是为什么 Docker 是 Kubernetes 项目的一个重要依赖。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span></strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>微服务架构</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果今天的软件并不是特别复杂并且需要承载的峰值流量不是特别多,那么后端项目的部署其实也只需要在虚拟机上安装一些简单的依赖,将需要部署的项目编译后运行就可以了。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.388" data-s="300,640" src="/upload/c1650b5c2872ccfaca151f200af68e67.png" data-type="png" data-w="1000" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但是随着软件变得越来越复杂,一个完整的后端服务不再是单体服务,而是由多个职责和功能不同的服务组成。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">服务之间复杂的拓扑关系以及单机已经无法满足性能需求使得软件的部署和运维工作变得非常复杂,这也就使得部署和运维大型集群变成了非常迫切的需求。</span><br></p> <p style="line-height: normal;"><br></p> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">小结:</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">Kubernetes 的出现不仅主宰了容器编排的市场,更改变了过去的运维方式,不仅将开发与运维之间边界变得更加模糊,而且让 DevOps 这一角色变得更加清晰。</span></h3> <h3 style="line-height: normal;"><br></h3> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">每一个软件工程师都可以通过 Kubernetes 来定义服务之间的拓扑关系、线上的节点个数、资源使用量并且能够快速实现水平扩容、蓝绿部署等在过去复杂的运维操作。</span></h3> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">Kuberentes 设计理念及架构</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span></strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>设计理念</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">我们先介绍 Kubernetes 的一些设计理念,这些关键字能够帮助了解 Kubernetes 在设计时所做的一些选择:</span></p> <figure> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.23333333333333334" data-s="300,640" src="/upload/3d62449e57f7b2dd68c24048f9aa2ea9.png" data-type="png" data-w="1200" style=""></p> </figure> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这里将按照顺序分别介绍声明式、显式接口、无侵入性和可移植性这几个设计的选择能够为我们带来什么。</span></p> <p style="line-height: normal;"><br></p> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 16px;"><strong><span style="color: rgb(89, 89, 89);letter-spacing: 1px;">声明式</span></strong></span></h3> <p style="line-height: normal;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">声明式(Declarative)的编程方式一直都会被工程师们拿来与命令式(Imperative)进行对比,这两者是完全不同的编程方法。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">我们最常接触的其实是命令式编程,它要求我们描述为了达到某一个效果或者目标所需要完成的指令,常见的编程语言 Go、Ruby、C++ 都为开发者提供了命令式的编程方法。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在 Kubernetes 中,我们可以直接使用 YAML 文件定义服务的拓扑结构和状态:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs makefile" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);"><span class="hljs-section" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">apiVersion: v1</span><br><span class="hljs-section" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">kind: Pod</span><br><span class="hljs-section" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">metadata:</span><br> name: rss-site<br> labels:<br> app: web<br><span class="hljs-section" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">spec:</span><br> containers:<br> - name: front-end<br> image: nginx<br> ports:<br> - containerPort: 80<br> - name: rss-reader<br> image: nickchase/rss-php-nginx:v1<br> ports:<br> - containerPort: 88<br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这种声明式的方式能够大量地减少使用者的工作量,极大地增加开发的效率。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这是因为声明式能够简化需要的代码,减少开发人员的工作,如果我们使用命令式的方式进行开发,虽然在配置上比较灵活,但是带来了更多的工作。</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs sql" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">SELECT</span> * <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">FROM</span> posts <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">WHERE</span> user_id = <span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">1</span> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">AND</span> title <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">LIKE</span> <span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">'hello%'</span>;<br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">SQL 其实就是一种常见的声明式『编程语言』,它能够让开发者自己去指定想要的数据是什么。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kubernetes 中的 YAML 文件也有着相同的原理,我们可以告诉 Kubernetes 想要的最终状态是什么,而它会帮助我们从现有的状态进行迁移。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.225" data-s="300,640" src="/upload/72f3536e4bae594daf5d6469f75f5d36.png" data-type="png" data-w="1200" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果 Kubernetes 采用命令式编程的方式提供接口,那么工程师可能就需要通过代码告诉 Kubernetes 要达到某个状态需要通过哪些操作,相比于更关注状态和结果声明式的编程方式,命令式的编程方式更强调过程。</span><br></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">总而言之,Kubernetes 中声明式的 API 其实指定的是集群期望的运行状态。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所以在出现任何不一致问题时,它本身都可以通过指定的 YAML 文件对线上集群进行状态的迁移。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">就像一个水平触发的系统,哪怕系统错过了相应的事件,最终也会根据当前的状态自动做出合适的操作。</span></p> <p style="line-height: normal;"><br></p> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 16px;"><strong><span style="color: rgb(89, 89, 89);letter-spacing: 1px;">显式接口</span></strong></span></h3> <p style="line-height: normal;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第二个 Kubernetes 的设计规范其实就是:不存在内部的私有接口,所有的接口都是显示定义的,组件之间通信使用的接口对于使用者来说都是显式的,我们都可以直接调用。</span></p> <figure> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.225" data-s="300,640" src="/upload/78913cbebd0b9c7bab2a5ee405cbe2dd.png" data-type="png" data-w="1200" style=""></p> </figure> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当 Kubernetes 的接口不能满足工程师的复杂需求时,我们需要利用已有的接口实现更复杂的特性,在这时 Kubernetes 的这一设计就不会成为自定义需求的障碍。</span></p> <p style="line-height: normal;"><br></p> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 16px;"><strong><span style="color: rgb(89, 89, 89);letter-spacing: 1px;">无侵入性</span></strong></span></h3> <p style="line-height: normal;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">为了尽可能满足用户(工程师)的需求,减少工程师的工作量与任务并增强灵活性,</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">Kubernetes 为工程师提供了无侵入式的接入方式。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">每一个应用或者服务一旦被打包成了镜像就可以直接在 Kubernetes 中无缝使用,不需要修改应用程序中的任何代码。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.30833333333333335" data-s="300,640" src="/upload/acd99489c1c57672acb9fb2107d2d954.png" data-type="png" data-w="1200" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Docker 和 Kubernetes 就像包裹在应用程序上的两层,它们两个为应用程序提供了容器化以及编排的能力。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在应用程序内部却不需要任何的修改就能够在 Docker 和 Kubernetes 集群中运行,这是 Kubernetes 在设计时选择无侵入带来最大的好处,同时无侵入的接入方式也是目前几乎所有应用程序或者服务都必须考虑的一点。</span><br></p> <p style="line-height: normal;"><br></p> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 16px;"><strong><span style="color: rgb(89, 89, 89);letter-spacing: 1px;">可移植性</span></strong></span></h3> <p style="line-height: normal;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在微服务架构中,我们往往都会让所有处理业务的服务变成无状态的服务。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">以前在内存中存储的数据、Session 等缓存,现在都会放到 Redis、ETCD 等数据库中存储,微服务架构要求我们对业务进行拆分并划清服务之间的边界,所以有状态的服务往往会对架构的水平迁移带来障碍。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">然而有状态的服务其实是无可避免的,我们将每一个基础服务或者业务服务都变成了一个个只负责计算的进程。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但是仍然需要有其他的进程负责存储易失的缓存和持久的数据,Kubernetes 对这种有状态的服务也提供了比较好的支持。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kubernetes 引入了 Persistent Volume 和 Persistent Volume Claim 的概念用来屏蔽底层存储的差异性,目前的 Kubernetes 支持下列类型的 Persistent Volume:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.38333333333333336" data-s="300,640" src="/upload/b1dc7f0432682be68a8711dc9e3543f8.png" data-type="png" data-w="1200" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这些不同的 Persistent Volume 会被开发者声明的 Persistent Volume Claim 分配到不同的服务中。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于上层来讲所有的服务都不需要接触 Persistent Volume,只需要直接使用 Persistent Volume Claim 得到的卷就可以了。</span><br></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span></strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>架构</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kubernetes 遵循非常传统的客户端服务端架构,客户端通过 RESTful 接口或者直接使用 kubectl 与 Kubernetes 集群进行通信。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这两者在实际上并没有太多的区别,后者也只是对 Kubernetes 提供的 RESTful API 进行封装并提供出来。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.3458333333333333" data-s="300,640" src="/upload/78c6afa8cec315bcdda05372f0c36fd9.png" data-type="png" data-w="1200" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">每一个 Kubernetes 集群都由一组 Master 节点和一系列的 Worker 节点组成,其中 Master 节点主要负责存储集群的状态并为 Kubernetes 对象分配和调度资源。</span><br></p> <p style="line-height: normal;"><br></p> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 16px;"><strong><span style="color: rgb(89, 89, 89);letter-spacing: 1px;">Master</span></strong></span></h3> <p style="line-height: normal;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">作为管理集群状态的 Master 节点,它主要负责接收客户端的请求,安排容器的执行并且运行控制循环,将集群的状态向目标状态进行迁移,Master 节点内部由三个组件构成:</span></p> <figure> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.475" data-s="300,640" src="/upload/7dd9a0ef26da0b5df597d309904bbff2.png" data-type="png" data-w="1200" style=""></p> </figure> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">其中 API Server 负责处理来自用户的请求,其主要作用就是对外提供 RESTful 的接口,包括用于查看集群状态的读请求以及改变集群状态的写请求,也是唯一一个与 etcd 集群通信的组件。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">而 Controller 管理器运行了一系列的控制器进程,这些进程会按照用户的期望状态在后台不断地调节整个集群中的对象,当服务的状态发生了改变,控制器就会发现这个改变并且开始向目标状态迁移。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最后的 Scheduler 调度器其实为 Kubernetes 中运行的 Pod 选择部署的 Worker 节点,它会根据用户的需要选择最能满足请求的节点来运行 Pod,它会在每次需要调度 Pod 时执行。</span></p> <p style="line-height: normal;"><br></p> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 16px;"><strong><span style="color: rgb(89, 89, 89);letter-spacing: 1px;">Worker</span></strong></span></h3> <p style="line-height: normal;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">其他的 Worker 节点实现就相对比较简单了,它主要由 kubelet 和 kube-proxy 两部分组成:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.5" data-s="300,640" src="/upload/7237cfdbc9a8b51810e208101108e398.png" data-type="png" data-w="1200" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">kubelet 是一个节点上的主要服务,它周期性地从 API Server 接受新的或者修改的 Pod 规范并且保证节点上的 Pod 和其中容器的正常运行,还会保证节点会向目标状态迁移,该节点仍然会向 Master 节点发送宿主机的健康状况。</span><br></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">另一个运行在各个节点上的代理服务 kube-proxy 负责宿主机的子网管理,同时也能将服务暴露给外部,其原理就是在多个隔离的网络中把请求转发给正确的 Pod 或者容器。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">Kubernetes 实现原理</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">到现在,我们已经对 Kubernetes 有了一些简单的认识和了解,也大概清楚了 Kubernetes 的架构,下面我们将介绍 Kubernetes 中的一些重要概念和实现原理。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span></strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>对象</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kubernetes 对象是系统中的持久实体,它使用这些对象来表示集群中的状态,这些对象能够描述:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.3416666666666667" data-s="300,640" src="/upload/51715b1000c929964b7aa98b542f93a4.png" data-type="png" data-w="1200" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这些对象描述了哪些应用应该运行在集群中,它们请求的资源下限和上限以及重启、升级和容错的策略。</span><br></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">每一个创建的对象都是我们对集群状态的改变,这些对象描述的其实就是集群的期望状态,Kubernetes 会根据我们指定的期望状态不断检查对当前的集群状态进行迁移。</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs cpp" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);">type Deployment <span class="hljs-class" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">struct</span> {</span><br> metav1.TypeMeta `json:<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">",inline"</span>`<br> metav1.ObjectMeta `json:<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"metadata,omitempty"</span> protobuf:<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"bytes,1,opt,name=metadata"</span>`<br><br> Spec DeploymentSpec `json:<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"spec,omitempty"</span> protobuf:<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"bytes,2,opt,name=spec"</span>`<br> Status DeploymentStatus `json:<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"status,omitempty"</span> protobuf:<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"bytes,3,opt,name=status"</span>`<br>}<br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">每一个对象都包含两个嵌套对象来描述规格(Spec)和状态(Status),对象的规格其实就是我们期望的目标状态。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">而状态描述了对象的当前状态,这部分一般由 Kubernetes 系统本身提供和管理,是我们观察集群本身的一个接口。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span></strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>Pod</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Pod 是 Kubernetes 中最基本的概念,它也是 Kubernetes 对象模型中我们可以创建或者部署的最小并且最简单的单元。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.4083333333333333" data-s="300,640" src="/upload/26ab998f561381ade3f87b780d12fddf.png" data-type="png" data-w="1200" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">它将应用的容器、存储资源以及独立的网络 IP 地址等资源打包到了一起,表示一个最小的部署单元。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但是每一个 Pod 中的运行的容器可能不止一个,这是因为 Pod 最开始设计时就能够在多个进程之间进行协调,构建一个高内聚的服务单元,这些容器能够共享存储和网络,非常方便地进行通信。</span><br></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span></strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>控制器</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最后要介绍的就是 Kubernetes 中的控制器,它们其实是用于创建和管理 Pod 的实例,能够在集群的曾名提供复制、发布以及健康检查的功能,这些控制器都运行在 Kubernetes 集群的主节点上。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在 Kuberentes 的 kubernetes/pkg/controller/ 目录中包含了官方提供的一些常见控制器,我们可以通过下面这个函数看到所有需要运行的控制器:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs go" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);"><span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">func</span> <span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">NewControllerInitializers</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">(loopMode ControllerLoopMode)</span> <span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">map</span>[<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">string</span>]<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">InitFunc</span></span> {<br> controllers := <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">map</span>[<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">string</span>]InitFunc{}<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"endpoint"</span>] = startEndpointController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"replicationcontroller"</span>] = startReplicationController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"podgc"</span>] = startPodGCController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"resourcequota"</span>] = startResourceQuotaController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"namespace"</span>] = startNamespaceController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"serviceaccount"</span>] = startServiceAccountController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"garbagecollector"</span>] = startGarbageCollectorController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"daemonset"</span>] = startDaemonSetController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"job"</span>] = startJobController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"deployment"</span>] = startDeploymentController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"replicaset"</span>] = startReplicaSetController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"horizontalpodautoscaling"</span>] = startHPAController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"disruption"</span>] = startDisruptionController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"statefulset"</span>] = startStatefulSetController<br> controllers[<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"cronjob"</span>] = startCronJobController<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">// ...</span><br><br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">return</span> controllers<br>}<br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这些控制器会随着控制器管理器的启动而运行,它们会监听集群状态的变更来调整集群中的 Kuberentes 对象的状态,在后面的文章中我们会展开介绍一些常见控制器的实现原理。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">总结</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">通过上文我们已经了解了它出现的背景、依赖的关键技术,同时我们也介绍了 Kubernetes 的架构设计,主节点负责处理客户端的请求、节点的调度,最后我们提到了几个 Kuberentes 中非常重要的概念:对象、Pod 和控制器。</span></p> <p style="line-height: normal;"><br></p> <p style="white-space: normal;text-align: justify;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);letter-spacing: 1px;"><em><span style="font-size: 14px;">作者:<span style="line-height: 25.6px;">Draveness</span></span></em></span></p> <p style="white-space: normal;text-align: justify;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);letter-spacing: 1px;"><em><span style="font-size: 14px;">编辑:陶家龙、孙淑娟</span></em></span><br></p> <p style="text-align: justify;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;"><em>出处:转载自真没什么逻辑(ID:draveness)微信公众号。</em></span></p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.3939393939393939" src="/upload/70dc65115d35e7d4c78cd84ded7bacac.gif" data-type="gif" data-w="660" style=""></p> <p style="text-align: center;"><a class="weapp_image_link" data-miniprogram-appid="wxc58e6e4759a08388" data-miniprogram-path="pages/bkdetail?id=244" data-miniprogram-nickname="51CTO播客" href=""><img class="" data-ratio="0.45" data-s="300,640" src="/upload/9018009f2ea29fb8d835fe1652feeff9.jpg" data-type="jpeg" data-w="680" style=""></a></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;box-sizing: border-box;"> <section style="font-size: 15px;border-style: solid;border-width: 0px 0px 1px;color: rgb(89, 89, 89);border-bottom-color: rgba(215, 215, 215, 0.960784);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;"><strong>精彩文章推荐:</strong></span></p> </section> </section> </section> </section> <p style="text-align: justify;line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655821473&idx=1&sn=117c10149c12db39f077854da3d97e76&chksm=bd74d3768a035a602eeca77072c385a75819975620e88a1999fefb90163a94b53b440519bd42&scene=21#wechat_redirect" target="_blank" style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;text-decoration: none;" data-linktype="2"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;">过于真实!《互联网公司迷信大全》</span></a><br></p> <p style="text-align: justify;line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655821605&idx=1&sn=c195b055f52037dea128a712e6a93b43&chksm=bd74d2f28a035be4a913d92281f6e9e6e13f26a84d930e172499821f2ea89a678f7cb3ad6374&scene=21#wechat_redirect" target="_blank" style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;text-decoration: none;" data-linktype="2"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;">老板让我写个Bug,这可咋整?</span></a><br></p> <p style="text-align: justify;line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655821541&idx=1&sn=01c187372ce52ed7bc51ce5c9679e1ea&chksm=bd74d3328a035a248effe13126b4f81780daf538a77cc43cd8f3aa3ce69b145f6fe022f6c4cb&scene=21#wechat_redirect" target="_blank" style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;text-decoration: none;" data-linktype="2"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;">掌握Nginx监控运维,这一篇足矣!</span></a><br></p>
作者:微信小助手
<p style="box-sizing: border-box;font-size: 14px;color: rgb(62, 62, 62);margin-bottom: 15px;line-height: 2em;white-space: normal;"><img class="" data-ratio="0.5625" src="/upload/e6cbd1c8682a360f534ce4f6e3145d64.jpg" data-type="jpeg" data-w="720" style="box-sizing: border-box;border-width: 0px;border-style: initial;border-color: initial;"></p> <h2 style="box-sizing: border-box;margin-top: 2rem;margin-bottom: 1rem;color: rgb(21, 153, 87);white-space: normal;"><span style="font-size: 18px;"><strong>前言</strong></span></h2> <p style="box-sizing: border-box;font-size: 14px;color: rgb(62, 62, 62);margin-bottom: 15px;margin-top: 15px;line-height: 2em;white-space: normal;">在一个阳光明媚的下午,电脑右下角传来一片片邮件提醒,同时伴随着微信钉钉的震动,打开一看,应用各种出错,天兔告警,数据库服务器内存爆红,MySql 数据库实例挂掉了。</p> <h2 style="box-sizing: border-box;margin-top: 2rem;margin-bottom: 1rem;color: rgb(21, 153, 87);white-space: normal;"><span style="font-size: 18px;"><strong>排查</strong></span></h2> <p style="box-sizing: border-box;font-size: 14px;color: rgb(62, 62, 62);margin-bottom: 15px;margin-top: 15px;line-height: 2em;white-space: normal;">先交代一下数据库版本:</p> <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;overflow: auto;font-family: Consolas, Menlo, Courier, monospace;font-size: 10px;background-color: rgb(29, 31, 33);border-width: 1px;border-style: solid;border-color: rgb(136, 136, 136);padding: 2px;color: rgb(80, 97, 109);line-height: 12px;"> <ol class="linenums list-paddingleft-2" style=""> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span class="pln" style="box-sizing: border-box;color: rgb(197, 200, 198);">mysql</span><span class="pun" style="box-sizing: border-box;color: rgb(197, 200, 198);">></span><span class="pln" style="box-sizing: border-box;color: rgb(197, 200, 198);"> status</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span class="pun" style="box-sizing: border-box;color: rgb(197, 200, 198);">--------------</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span class="pln" style="box-sizing: border-box;color: rgb(197, 200, 198);">mysql </span><span class="typ" style="box-sizing: border-box;color: rgb(129, 162, 190);">Ver</span><span class="pln" style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span class="lit" style="box-sizing: border-box;color: rgb(222, 147, 95);">14.14</span><span class="pln" style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span class="typ" style="box-sizing: border-box;color: rgb(129, 162, 190);">Distrib</span><span class="pln" style="box-sizing: border-box;color: rgb(197, 200, 198);">
作者:じ☆ve宝贝
jfinal增加文件上传功能后其他参数值为null!在表单中增加enctype="multipart/form-data"后,前台往后台传参为null的情况? 解决办法: ` 在后台接受参数前必须先处理文件上传 ` ``` UploadFile uploadFile = getFile(); if(null != uploadFile){ String directory = uploadFile.getSaveDirectory(); String name = uploadFile.getFileName(); String path = "/"+ UUID.randomUUID().toString().replaceAll("-", "") + name.substring(name.lastIndexOf("."), name.length()); uploadFile.getFile().renameTo(new File(directory + path)); } News news = getModel(News.class); news.update(); ```
作者:微信小助手
<section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="padding-top: 10px;padding-right: 10px;padding-left: 10px;box-sizing: border-box;background-color: rgb(239, 239, 239);"> <span style="display: inline-block;width: 5%;line-height: 0.8;font-weight: bolder;font-size: 48px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> “ </section></span> <section style="display: inline-block;vertical-align: top;float: right;width: 90%;line-height: 1.5;font-size: 15px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;">在上篇文章<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655817476&idx=1&sn=0a50d49549e3efbeb7a7cf4903e410f3&chksm=bd74c2d38a034bc53b808d9dcba840f958c2c856a256cb21702a2945b7e02f86b9e2702eb671&scene=21#wechat_redirect" target="_blank">《深入学习 Redis 高可用的基石:主从复制》</a>中曾提到,Redis 主从复制的作用有数据热备、负载均衡、故障恢复等;但主从复制存在的一个问题是故障恢复无法自动化。</span></p> </section> <section style="clear: both;box-sizing: border-box;"></section> </section> </section> </section> </section> <p style="line-height: 1.75em;"><br></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.5593667546174143" data-s="300,640" src="/upload/adc89af5c55d348b0e99b7cb9166fb93.png" data-type="png" data-w="758" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">本文将要介绍的哨兵,它基于 Redis 主从复制,主要作用便是解决主节点故障恢复的自动化问题,进一步提高系统的高可用性。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">文章将首先介绍哨兵的作用和架构;然后讲述哨兵系统的部署方法,以及通过客户端访问哨兵系统的方法;然后简要说明哨兵实现的基本原理;最后给出关于哨兵实践的一些建议。(注:文章内容基于 Redis 3.0 版本<span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;line-height: 25.6px;">)</span></span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">哨兵的作用和架构</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>哨兵的作用</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在介绍哨兵之前,首先从宏观角度回顾一下 Redis 实现高可用相关的技术。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">它们包括:持久化、复制、哨兵和集群,其主要作用和解决的问题是:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;margin-left: 8px;margin-right: 8px;"> <li><p style="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">持久化:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">复制:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">复制是高可用 Redis 的基础,哨兵和集群都是在复制基础上实现高可用的。</span></p><p style="text-align: justify;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">哨兵:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载均衡;存储能力受到单机的限制。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">集群:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">通过集群,Redis 解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">下面说回哨兵,<span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;line-height: 1.6;">Redis Sentinel,即 Redis 哨兵,在 Redis 2.8 版本开始引入。哨兵的核心功能是主节点的自动故障转移。</span></span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;line-height: 1.6;letter-spacing: 1px;color: rgb(71, 193, 168);">下面是 Redis 官方文档对于哨兵功能的描述:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;margin-left: 8px;margin-right: 8px;"> <li><p style="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">监控(Monitoring):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">哨兵会不断地检查主节点和从节点是否运作正常。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">自动故障转移(Automatic failover):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">配置提供者(Configurationprovider):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">客户端在初始化时,通过连接哨兵来获得当前 Redis 服务的主节点地址。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">通知(Notification):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">哨兵可以将故障转移的结果发送给客户端。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">其中,监控和自动故障转移功能,使得哨兵可以及时发现主节点故障并完成转移;而配置提供者和通知功能,则需要在与客户端的交互中才能体现。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这里对“客户端”一词在本文的用法做一个说明:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在前面的文章中,只要通过 API 访问 Redis 服务器,都会称作客户端,包括 redis-cli、Java 客户端 Jedis 等。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">为了便于区分说明,本文中的客户端并不包括 redis-cli,而是比 redis-cli 更加复杂。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">redis-cli 使用的是 Redis 提供的底层接口,而客户端则对这些接口、功能进行了封装,以便充分利用哨兵的配置提供者和通知功能。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>哨兵的架构</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">典型的哨兵架构图如下所示:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.7688888888888888" data-s="300,640" src="/upload/3e0966a7937411e89b4a818a307d5938.png" data-type="png" data-w="450" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">它由两部分组成,哨兵节点和数据节点:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;margin-left: 8px;margin-right: 8px;"> <li><p style="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">哨兵节点:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的 Redis 节点,不存储数据。</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">数据节点:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">主节点和从节点都是数据节点。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">哨兵系统的部署方法</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这一部分将部署一个简单的哨兵系统,包含 1 个主节点、2 个从节点和 3 个哨兵节点。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">方便起见:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所有这些节点都部署在一台机器上(局域网 IP:192.168.92.128),使用端口号区分;节点的配置尽可能简化。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>部署主从节点</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">哨兵系统中的主从节点,与普通的主从节点配置是一样的,并不需要做任何额外配置。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">下面分别是主节点(port=6379)和 2 个从节点(port=6380/6381)的配置文件,配置都比较简单,不再详述。</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs css" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);"><span class="hljs-selector-id" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">#redis-6379</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.conf</span><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">port</span> 6379<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">daemonize</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">yes</span><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">logfile</span> "6379<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.log</span>"<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">dbfilename</span> "<span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">dump-6379</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.rdb</span>"<br><br><span class="hljs-selector-id" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">#redis-6380</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.conf</span><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">port</span> 6380<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">daemonize</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">yes</span><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">logfile</span> "6380<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.log</span>"<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">dbfilename</span> "<span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">dump-6380</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.rdb</span>"<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">slaveof</span> 192<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.168.92.128</span> 6379<br><br><span class="hljs-selector-id" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">#redis-6381</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.conf</span><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">port</span> 6381<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">daemonize</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">yes</span><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">logfile</span> "6381<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.log</span>"<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">dbfilename</span> "<span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">dump-6381</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.rdb</span>"<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">slaveof</span> 192<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.168.92.128</span> 6379<br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">配置完成后,依次启动主节点和从节点:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs css" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);"><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">redis-server</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">redis-6379</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.conf</span><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">redis-server</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">redis-6380</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.conf</span><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">redis-server</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">redis-6381</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.conf</span><br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">节点启动后,连接主节点查看主从状态是否正常,如下图所示:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><img class="" data-copyright="0" data-ratio="0.324459234608985" data-s="300,640" src="/upload/4bed95bb7b442adcd243533409302a48.png" data-type="png" data-w="601" style=""></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>部署哨兵节点</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">哨兵节点本质上是特殊的 Redis 节点。<span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;line-height: 1.6;">3 个哨兵节点的配置几乎是完全一样的,主要区别在于端口号的不同(26379/26380/26381)。</span></span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;line-height: 1.6;color: rgb(89, 89, 89);letter-spacing: 1px;">下面以 26379 节点为例,介绍节点的配置和启动方式,配置部分尽量简化,更多配置会在后面介绍。</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs css" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);"><span class="hljs-selector-id" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">#sentinel-26379</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.conf</span><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">port</span> 26379<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">daemonize</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">yes</span><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">logfile</span> "26379<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.log</span>"<br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">sentinel</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">monitor</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">mymaster</span> 192<span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.168.92.128</span> 6379 2<br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">其中,sentinel monitor mymaster 192.168.92.128 6379 2 配置的含义是:该哨兵节点监控 192.168.92.128:6379 这个主节点。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">该主节点的名称是 mymaster,最后的 2 的含义与主节点的故障判定有关:至少需要 2 个哨兵节点同意,才能判定主节点故障并进行故障转移。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">哨兵节点的启动有两种方式,二者作用是完全相同的:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs css" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: rgb(40, 43, 46);"><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">redis-sentinel</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">sentinel-26379</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.conf</span><br><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">redis-server</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">sentinel-26379</span><span class="hljs-selector-class" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">.conf</span> <span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">--sentinel</span><br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">按照上述方式配置和启动之后,整个哨兵系统就启动完毕了,可以通过 redis-cli 连接哨兵节点进行验证。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如下图所示:可以看出 26379 哨兵节点已经在监控 mymaster 主节点(即192.168.92.128:6379),并发现了其 2 个从节点和另外 2 个哨兵节点。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.1881720430107527" data-s="300,640" src="/upload/c5bddf2bc92552bbf6670d03bd904bb8.png" data-type="png" data-w="" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">此时如果查看哨兵节点的配置文件,会发现一些变化,以 26379 为例:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.3100358422939068" data-s="300,640" src="/upload/fbcff1243c79d510a3ed876f453da048.png" data-type="png" data-w="" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">其中,dir 只是显式声明了数据和日志所在的目录(在哨兵语境下只有日志);known-slave 和 known-sentinel 显示哨兵已经发现了从节点和其他哨兵。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">带有 epoch 的参数与配置纪元有关(配置纪元是一个从 0 开始的计数器,每进行一次领导者哨兵选举,都会 +1;领导者哨兵选举是故障转移阶段的一个操作,在后文原理部分会介绍)。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89,
作者:じ☆ve宝贝
使用纯真ip的qqwry.dat作为库来验证ip地址的具体位置信息 ``` package cn.studyjava; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.net.URL; public final class IPParser { private static final String DATA_PATH = "cn/studyjava/qqwry.dat"; // 获取指定IP的城市 public static String getCountry(String ip) { return new SubIPParser(DATA_PATH).seek(ip).getCountry(); } // 获取指定IP的位置 public static String getLocal(String ip) { return new SubIPParser(DATA_PATH).seek(ip).getLocation(); } // 获取指定IP的城市+位置 public static String getForSeparator(String ip, String sep) { SubIPParser parser = new SubIPParser(DATA_PATH).seek(ip); return parser.getCountry() + sep + parser.getLocation(); } private IPParser() {} // 对原版代码做了重构,逻辑保持不变; static class SubIPParser { private String dataClasspath; private String country; private String location; private int recordCount, countryFlag; private long rangE, rangB, offSet, startIP, endIP, firstStartIP, lastStartIP, endIPOff; public SubIPParser(String classpath) { dataClasspath = classpath; } public SubIPParser seek(String ip) { RandomAccessFile fis = null; byte[] buff = null; long ipn; try { ipn = ipToLong(ip); fis = new RandomAccessFile(getDataPath().getFile(), "r"); buff = new byte[4]; fis.seek(0); fis.read(buff); firstStartIP = this.byteToLong(buff); fis.read(buff); lastStartIP = this.byteToLong(buff); recordCount = (int) ((lastStartIP - firstStartIP) / 7); if (recordCount <= 1) { location = country = "未知"; return this; } rangB = 0; rangE = recordCount; long RecNo; do { RecNo = (rangB + rangE) / 2; loadStartIP(RecNo, fis); if (ipn == startIP) { rangB = RecNo; break; } if (ipn > startIP) rangB = RecNo; else rangE = RecNo; } while (rangB < rangE - 1); loadStartIP(rangB, fis); loadEndIP(fis); loadCountry(ipn, fis); } catch (Exception e) { throw new RuntimeException(e); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { } } } return this; } public String getLocation() { return this.location; } public String getCountry() { return this.country; } private long byteToLong(byte[] b) { long ret = 0; for (int i = 0; i < b.length; i++) { long t = 1L; for (int j = 0; j < i; j++) { t = t * 256L; } ret += ((b[i] < 0) ? 256 + b[i] : b[i]) * t; } return ret; } private long ipToLong(String ip) { String[] arr = ip.split("\\."); long ret = 0; for (int i = 0; i < arr.length; i++) { long l = 1; for (int j = 0; j < i; j++) l *= 256; try { ret += Long.parseLong(arr[arr.length - i - 1]) * l; } catch (Exception e) { ret += 0; } } return ret; } private URL getDataPath() { URL url = null; url = Thread.currentThread().getContextClassLoader().getResource(dataClasspath); if (url == null) { url = IPParser.class.getClassLoader().getResource(dataClasspath); } return url; } private String getFlagStr(long OffSet, RandomAccessFile fis) throws IOException { int flag = 0; byte[] buff = null; do { fis.seek(OffSet); buff = new byte[1]; fis.read(buff); flag = (buff[0] < 0) ? 256 + buff[0] : buff[0]; if (flag == 1 || flag == 2) { buff = new byte[3]; fis.read(buff); if (flag == 2) { countryFlag = 2; endIPOff = OffSet - 4; } OffSet = this.byteToLong(buff); } else break; } while (true); if (OffSet < 12) { return ""; } else { fis.seek(OffSet); return getText(fis); } } private String getText(RandomAccessFile fis) throws IOException { long len = fis.length(); ByteArrayOutputStream byteout = new ByteArrayOutputStream(); byte ch = fis.readByte(); do { byteout.write(ch); ch = fis.readByte(); } while (ch != 0 && fis.getFilePointer() < len); return byteout.toString("gbk"); } private void loadCountry(long ipn, RandomAccessFile fis) throws IOException { if (countryFlag == 1 || countryFlag == 2) { country = getFlagStr(endIPOff + 4, fis); if (countryFlag == 1) { location = getFlagStr(fis.getFilePointer(), fis); if (ipn >= ipToLong("255.255.255.0") && ipn <= ipToLong("255.255.255.255")) { location = getFlagStr(endIPOff + 21, fis); country = getFlagStr(endIPOff + 12, fis); } } else { location = getFlagStr(endIPOff + 8, fis); } } else { country = getFlagStr(endIPOff + 4, fis); location = getFlagStr(fis.getFilePointer(), fis); } } private long loadEndIP(RandomAccessFile fis) throws IOException { byte[] buff = null; fis.seek(endIPOff); buff = new byte[4]; fis.read(buff); endIP = this.byteToLong(buff); buff = new byte[1]; fis.read(buff); countryFlag = (buff[0] < 0) ? 256 + buff[0] : buff[0]; return endIP; } private long loadStartIP(long RecNo, RandomAccessFile fis) throws IOException { byte[] buff = null; offSet = firstStartIP + RecNo * 7; fis.seek(offSet); buff = new byte[4]; fis.read(buff); startIP = this.byteToLong(buff); buff = new byte[3]; fis.read(buff); endIPOff = this.byteToLong(buff); return startIP; } } public static void main(String[] args) throws Exception { long initUsedMemory = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()); long start = System.currentTimeMillis(); // 查询IP地址 System.out.println(IPParser.getForSeparator("125.34.19.135", ", ")); long end = System.currentTimeMillis(); long endUsedMemory = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()); // 性能测试 System.out.println("time spent:" + (end - start) + " ms"); System.out.println("memory consumes:" + (endUsedMemory - initUsedMemory) / 1024 + " kb"); } } ```
作者:微信小助手
<p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">前言</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">今日早读文章由阿里妈妈@紫云飞授权分享。</p> <blockquote style="box-sizing: border-box;margin: 0px 0px 1.125em;padding: 0px 15px 0px 20px;color: rgb(102, 102, 102);border-left: 4px solid rgb(221, 221, 221);font-size: 14px;font-style: italic;font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;"> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;font-size: 1em;">本文首发于阿里妈妈前端快爆</p> </blockquote> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">正文从这开始~~</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">在 HTTP 客户端(浏览器或者缓存服务器)上,如果某个 URL 对应的缓存过期了,客户端会再次向该 URL 发送一个条件请求(带有If-Modified-Since/If-None-Match请求头),如果服务端(缓存服务器或者源站)返回的状态码是 304(没有响应体),则客户端会根据该304响应所包含的一些响应头(Date、Last-Modified、Cache-Control等)重新计算出这条缓存的过期时间,比如:</p> <pre class="prettyprint prettyprinted" style="box-sizing: border-box;font-family: Consolas, Menlo, Courier, monospace;font-size: 13.6px;margin-top: 0px;margin-bottom: 16px;overflow: auto;padding: 10px;line-height: 1.6;background: rgb(246, 246, 246) none repeat scroll 0% 0%;border-radius: 3px;overflow-wrap: break-word;border-color: rgb(221, 221, 221);border-style: solid;border-width: 1px;white-space: pre-wrap;color: rgb(51, 51, 51);font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;text-align: left;text-indent: 0px;text-transform: none;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;"><code style="box-sizing: border-box;font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;font-size: 13.6px;padding: 0px;margin: 0px;background: transparent none repeat scroll 0% 0%;border-radius: 3px;border-color: currentcolor;border-style: none;border-width: 0px;word-break: normal;white-space: pre;display: inline;max-width: initial;overflow: initial;line-height: inherit;overflow-wrap: normal;"><span class="pln" style="box-sizing: border-box;color: rgb(0, 0, 0);">HTTP</span><span class="pun" style="box-sizing: border-box;color: rgb(102, 102, 0);">/</span><span class="lit" style="box-sizing: border-box;color: rgb(0, 102, 102);">2</span><span class="pln" style="box-sizing: border-box;color: rgb(0, 0, 0);"> </span><span class="lit" style="box-sizing: border-box;color: rgb(0, 102, 102);">304</span><span class="pln" style="box-sizing: border-box;color: rgb(0, 0, 0);"><br style="box-sizing: border-box;"></span><span class="typ" style="box-sizing: border-box;color: rgb(102, 0, 102);">Cache</span><span class="pun" style="box-sizing: border-box;color: rgb(102, 102, 0);">-</span><span class="typ" style="box-sizing: border-box;color: rgb(102, 0, 102);">Control</span><span class="pun" style="box-sizing: border-box;color: rgb(102, 102, 0);">:</span><span class="pln" style="box-sizing: border-box;color: rgb(0, 0, 0);"> max</span><span class="pun" style="box-sizing: border-box;color: rgb(102, 102, 0);">-</span><span class="pln" style="box-sizing: border-box;color: rgb(0, 0, 0);">age</span><span class="pun" style="box-sizing: border-box;color: rgb(102, 102, 0);">=</span><span class="lit" style="box-sizing: border-box;color: rgb(0, 102, 102);">86400</span></code></pre> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">这样的 304响应,就能让这条缓存重新续命一天;如果返回的状态码是 200,则整条缓存会被新返回的响应体替换掉。无论是哪种情况,这条缓存都重新变的有效了,HTTP 规范里把这一“让过期的缓存重新变的有效”过程,叫做 revalidate,英语翻译过来应该是“使重新生效”。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">不过使动用法总是让人不好记忆,你也可以把 revalidate 理解成“再次校验”的意思:再次校验看看缓存是不是真的过期了,真过期了的话返回 200,假过期(客户端判断为过期了,但服务端说并没有)的话返回 304。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">所以现在知道了,revalidate 是个常见的动作,缓存过期就会 revalidate ,缓存过期就会 revalidate ,缓存过期就会 revalidate ,说三遍,revalidate 不需要专门的指令。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">搞清楚了 revalidate 这个词的含义,那么再来推测一下 must-revalidate是用来做什么的。must-revalidate直译过来是“必须再次校验”,那是不是说每次使用缓存前都要先校验一遍?即便没有过期?于是很多人按照这样的推测,写出了如下的 Cache-Control头:</p> <pre class="prettyprint prettyprinted" style="box-sizing: border-box;font-family: Consolas, Menlo, Courier, monospace;font-size: 13.6px;margin-top: 0px;margin-bottom: 16px;overflow: auto;padding: 10px;line-height: 1.6;background: rgb(246, 246, 246) none repeat scroll 0% 0%;border-radius: 3px;overflow-wrap: break-word;border-color: rgb(221, 221, 221);border-style: solid;border-width: 1px;white-space: pre-wrap;color: rgb(51, 51, 51);font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;text-align: left;text-indent: 0px;text-transform: none;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;"><code style="box-sizing: border-box;font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;font-size: 13.6px;padding: 0px;margin: 0px;background: transparent none repeat scroll 0% 0%;border-radius: 3px;border-color: currentcolor;border-style: none;border-width: 0px;word-break: normal;white-space: pre;display: inline;max-width: initial;overflow: initial;line-height: inherit;overflow-wrap: normal;"><span class="typ" style="box-sizing: border-box;color: rgb(102, 0, 102);">Cache</span><span class="pun" style="box-sizing: border-box;color: rgb(102, 102, 0);">-</span><span class="typ" style="box-sizing: border-box;color: rgb(102, 0, 102);">Control</span><span class="pun" style="box-sizing: border-box;color: rgb(102, 102, 0);">:</span><span class="pln" style="box-sizing: border-box;color: rgb(0, 0, 0);"> max</span><span class="pun" style="box-sizing: border-box;color: rgb(102, 102, 0);">-</span><span class="pln" style="box-sizing: border-box;color: rgb(0, 0, 0);">age</span><span class="pun" style="box-sizing: border-box;color: rgb(102, 102, 0);">=</span><span class="lit" style="box-sizing: border-box;color: rgb(0, 102, 102);">86400</span><span class="pun" style="box-sizing: border-box;color: rgb(102, 102, 0);">,</span><span class="pln" style="box-sizing: border-box;color: rgb(0, 0, 0);"> must</span><span class="pun" style="box-sizing: border-box;color: rgb(102, 102, 0);">-</span><span class="pln" style="box-sizing: border-box;color: rgb(0, 0, 0);">revalidate</span></code></pre> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">写这个配置的人想表示的是:该缓存有效期为一天,在这一天内,每次使用缓存前要先校验一遍才能使用。可试试就知道了,这里的must-revalidate 并不会生效,这条缓存仍然是直接读取了本地。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">这是为什么呢?是因为must-revalidate生效有个前提,前提就是这个缓存必须已经过期,也就是说,必须一天以后,这个must-revalidate才可能发挥作用,规范里说的原话是:</p> <blockquote style="box-sizing: border-box;margin: 0px 0px 1.125em;padding: 0px 15px 0px 20px;color: rgb(102, 102, 102);border-left: 4px solid rgb(221, 221, 221);font-size: 14px;font-style: italic;font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;"> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;font-size: 1em;">The “must-revalidate” response directive indicates that once it has become stale, a cache MUST NOT use the response to satisfy subsequent requests without successful validation on the origin server.</p> </blockquote> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">翻译过来大概就是,must-revalidate指令是用来表示在一个缓存过期之后,不能直接使用这个过期的缓存,必须校验之后才能使用。哎?What?回忆一下刚才重复三遍的话:“缓存过期就会 revalidate”,revalidate 是缓存过期后自然而然的表现,怎么还需要专门的指令呢?</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">再细读规范就知道了,原来 must-revalidate生效的场景还有一个大前提,那就是 HTTP 规范是允许客户端在某些特殊情况下直接使用过期缓存的,比如校验请求发送失败的时候,还比如有配置一些特殊指令(stale-while-revalidate、stale-if-error等)的时候,原文是这样的:</p> <blockquote style="box-sizing: border-box;margin: 0px 0px 1.125em;padding: 0px 15px 0px 20px;color: rgb(102, 102, 102);border-left: 4px solid rgb(221, 221, 221);font-size: 14px;font-style: italic;font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;"> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;font-size: 1em;">A cache MUST NOT send stale responses unless it is disconnected (i.e., it cannot contact the origin server or otherwise find a forward path) or doing so is explicitly allowed</p> </blockquote> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">而must-revalidate的作用就是让那个“unless”失效 ,带有 must-revalidate 的缓存,在任何情况下,都必须成功 revalidate 后才能使用,没有例外。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">各种缓存服务器软件,比如 NGINX、Vanish、Squid 都或多或少的允许通过Cache-Control指令或者修改软件配置的方式返回过期缓存,同时它们也都遵循了 HTTP 规范,加上must-revalidate的确能阻止返回过期缓存的行为。国内各大 CDN 厂商应该用的都是自研软件,不确定支持不支持返回过期缓存,所以 must-revalidate在国内网络环境能不能派上用场也不太确定。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">更加不确定的是,很多用了 must-revalidate 指令的人真的知道它的作用是什么吗?现在回到我前面举的那个希望每次使用缓存前先校验一遍的例子:</p> <blockquote style="box-sizing: border-box;margin: 0px 0px 1.125em;padding: 0px 15px 0px 20px;color: rgb(102, 102, 102);border-left: 4px solid rgb(221, 221, 221);font-size: 14px;font-style: italic;font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;"> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;font-size: 1em;">Cache-Control: max-age=86400, must-revalidate</p> </blockquote> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">写出这个配置的人其实真正想要的是Cache-Control: no-cache。Cache-Control的几个指令特别容易混淆,不能望文生义。比如no-cache,并不是指不能用 cache,客户端仍会把带有 no-cache 的响应缓存下来,只不过每次不会直接用缓存,而得先 revalidate 一下,所以其实no-cache真正合适的名字才是 must-revalidate。而现在的must-revalidate更合适的名字可能是 never-return-stale。如果你想让客户端完全不缓存响应,应该用no-store,带有no-store的响应不会被缓存到任意的磁盘或者内存里,它才是真正的 no-cache。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">计算机领域有个名言警句:</p> <blockquote style="box-sizing: border-box;margin: 0px 0px 1.125em;padding: 0px 15px 0px 20px;color: rgb(102, 102, 102);border-left: 4px solid rgb(221, 221, 221);font-size: 14px;font-style: italic;font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;"> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;font-size: 1em;">There are only two hard problems in Computer Science: cache invalidation, and naming things.(计算机领域只有有两大难题,“让缓存失效”和“给东西命名”)</p> </blockquote> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">而给上面讲的这些 Cache-Control指令命名,就是在给 HTTP 缓存失效相关的东西命名,恰好是两个难题撞到一起了,难上加难,才造成了这乱糟糟的局面。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">读到这里你可能已经晕了,但本文才讲了一半。我上面只讲了must-revalidate在缓存服务器上的作用,还没说在浏览器上的作用。既然我说了must-revalidate更合适的名字是 never-return-stale,那浏览器有没有 return stale 的情况呢,也就是说浏览器会不会使用过期缓存呢?</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">还真有,那就是浏览器的后退前进功能。当点击 back/forwrad 按钮时,浏览器会尽量用本地缓存来重新打开页面,即便缓存已经过期了,也不会 revalidate。那must-revalidate能阻止这一行为,强迫该缓存 revalidate 吗?答案是并不能,甚至no-cache也不行,只有比no-cache更强劲的no-store才可以,因为硬盘上都没有缓存,浏览器想用也没法用啊。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">另外值得注意的是,如果真有上面这个需求,未来可能no-store也帮不了你,因为 Chrome 目前在实现 bfcache,如果实现了,在页面前进后退时,页面内容会直接从内存缓存里读取,页面甚至都不会重新加载,连 JS 变量都保存着上次的值。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">上面三段总结一下就是,must-revalidate的本职工作在浏览器端并没有发挥作用。那是不是说浏览器们就完全就不认什么 must-revalidate,连解析都没解析它?在 Firefox 和 Safari 里可能还真是这样的,但在 Chrome 里,不知道什么人手贱给 must-revalidate实现了一个规范里并没有要求的功能,下面我来说说到底是什么功能。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">想让一个资源能缓存,有三种方式,按照解析优先级排序如下:</p> <ul style="margin-left: 0px;margin-right: 0px;" class=" list-paddingleft-2"> <li><p>HTTP 1.1 风格的Cache-Control 响应头中的 max-age指令</p></li> <li><p>HTTP 1.0 风格的 Expires 响应头</p></li> <li><p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">Last-Modified响应头</p></li> </ul> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">很多人只知道前两个,还知道 1 比 2 优先级高,但不知道第 3 个,我举个例子:</p> <blockquote style="box-sizing: border-box;margin: 0px 0px 1.125em;padding: 0px 15px 0px 20px;color: rgb(102, 102, 102);border-left: 4px solid rgb(221, 221, 221);font-size: 14px;font-style: italic;font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;"> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;font-size: 1em;">HTTP/2 200<br style="box-sizing: border-box;">Date: Wed, 27 Mar 2019 22:00:00 GMT<br style="box-sizing: border-box;">Last-Modified: Wed, 27 Mar 2019 12:00:00 GMT</p> </blockquote> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">上面这个响应,没有Cache-Control,也没有 Expires,但它其实也可以被缓存,可缓存时长是用 Date响应头的时间减去Last-Modified的时间,得出的时长再除以10,用汉语描述的话,就是用这个文件最近一次更新到现在的十分之一时长作为可缓存时长,这个例子的话,计算出的可缓存时长是一小时。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">这种可缓存时长的算法用代码表示是 (date_value - last_modified_value) <em style="box-sizing: border-box;">0.10, 它是由 HTTP 规范推荐的算法,但规范中仅仅是推荐而已,并没有做强制要求,比如 Firefox 中就在这个算法的基础上还和 7 天时长取了一次最小值,是 min(one-week, (date_value - last_modified_value) </em>0.10) 。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">扯远了,我们把这第 3 种缓存方式叫做启发式的(heuristic)缓存,你可以理解是没有显示的用前两种方式设置缓存时长的话,浏览器就会用这种隐式的缓存方式。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">如果你想禁用由 Last-Modified响应头造成的启发式缓存,正确的做法是要加上 Cache-Control: no-cache,但在 Chrome 中,Cache-Control: must-revalidate也有同样的功效。很多人在 Chrome 里开发和测试,所以误以为这是 must-revalidate的正规作用,从而推断其它浏览器也是支持的,但其实规范里并没有指出 must-revalidate有关闭启发式缓存的功效,其它浏览器也都不支持这种用法。Chrome 里的这行代码加于 2008 年,看注释像是 Chrome 最早的开发工程师,现在 Chrome 的 VP Darin Fisher 写的。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;">好了,差不多讲完了, 再来句总结:must-revalidate在缓存服务器上有一点点作用,但比较小众;在浏览器端几乎没有任何作用。绝大多数情况,人们都是把它误用为 no-cache了。或者是完全没细研究,直接把max-age=0, no-cache, no-store, must-revalidate一坨都塞进去了,反正能 work 就不管了。</p> <p style="box-sizing: border-box;margin: 0px 0px 1.125em;color: rgb(51, 51, 51);font-family: "-apple-system Helvetica", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;font-size: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;"><span style="font-size: 14px;color: rgb(136, 136, 136);">关于本文</span><br style="box-sizing: border-box;"><span style="font-size: 14px;color: rgb(136, 136, 136);">作者:</span><span style="font-size: 14px;color: rgb(136, 136, 136);">@紫云飞</span><br style="box-sizing: border-box;"><span style="font-size: 14px;color: rgb(136, 136, 136);">原文:</span><span style="font-size: 14px;color: rgb(136, 136, 136);">https://zhuanlan.zhihu.com/p/60357719</span></p> <p style="text-align: center;"><img class="rich_pages" data-copyright="0" data-ratio="0.4166666666666667" data-s="300,640" src="/upload/1bb4d078513bcbd403618fb04848b24d.jpg" data-type="jpeg" data-w="720" style=""></p> <p>他曾分享过</p> <p><br></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651231826&idx=2&sn=0e61312763acb0f7fb027f90f0288d5b&chksm=bd494fd68a3ec6c0a0bdb48d89937aa2fb30eaad60022097baabd7dd6ad3408b8218927238ca&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2">【第1565期】浏览器中的画中画(Picture-in-Picture)模式及其 API</a></p> <p><br></p> <p>为你推荐</p> <p><br></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651231917&idx=2&sn=26b93810ab043e64f71f882f35d912c3&chksm=bd494f298a3ec63fc9b015c37e63756e75d8813d5b1b6984a9e27bcd9625d915e1ba06a84cc2&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2">【第1574期】浏览器帧原理剖析</a></p> <p><br></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651231568&idx=2&sn=c2cf30060f3b58cd9cc2a00b413922c1&chksm=bd494cd48a3ec5c2f86e9729538e7827ae147608bb35800b27a1d44a28406f89dfb538bdaf2c&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2">【第1541期】资源优先级 – 让浏览器助您一臂之力</a></p> <p><br></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651228726&idx=1&sn=a4bb943ec27ab7abeab73f2df45d953b&chksm=bd4953b28a3edaa439cad3f3d73e55d13dc045308400b48929bb64bfdc7148b4c0ac772a1686&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2">【第1285期】我知道的HTTP请求</a><br></p>
作者:微信小助手
<p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><span style="font-size: 14px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">群聊是多人社交的基本诉求,一个群友在群内发了一条消息,期望做到:</span></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><span style="font-size: 14px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">(1)<strong>在线的群友</strong>能</span><span style="margin: 0px;padding: 0px;color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 14px;">第一时间收到</span><span style="font-size: 14px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">消息;</span></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><span style="font-size: 14px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">(2)<strong>离线的群友</strong>能在</span><span style="margin: 0px;padding: 0px;color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 14px;">登陆后收到</span><span style="font-size: 14px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">消息;</span></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><span style="font-size: 14px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">群消息的实时性、可达性、离线消息<span style="background-color: transparent;color: rgb(51, 51, 51);display: inline;float: none;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;" px="px" none="none" justify="justify" normal="normal" yahei="yahei" ui="ui" gb="gb" sans="sans" sc="sc" neue="neue" helvetica="helvetica">的复杂度,要远高于单对单消息。</span></span></p> <p style="background-color: transparent;clea
作者:じ☆ve宝贝
### 安装依赖 ``` sudo yum install curl openssh-server postfix cronie #邮件服务器 sudo service postfix start sudo chkconfig postfix on ``` ### 邮件配置 ``` GitLab中使用postfix进行邮件发送。因此,可以卸载系统中自带的sendmail。 使用yum list installed查看系统中是否存在sendmail,若存在,则使用yum remove sendmail指令进行卸载。 ``` ``` 测试系统是否可以正常发送邮件。 echo "Test mail from postfix" | mail -s "Test Postfix" zsljava@studyjava.cn 注:上面的zsljava@studyjava.cn为你希望收到邮件的邮箱地址。当邮箱收到系统发送来的邮件时,将系统地址复制下来 例如:root@iZ23syflhhzZ.localdomain, 打开/etc/gitlab/gitlab.rb,将# gitlab_rails['gitlab_email_from'] = 'gitlab@example.com' ``` ### 安装 rpm -ivh gitlab-ee-8.0.4-ee.1.el6.x86_64.rpm 修改配置文件 vi /etc/gitlab/gitlab.rb, # gitlab_rails['gitlab_email_from'] = 'gitlab@example.com' 改为 gitlab_rails['gitlab_email_from'] = 'root@iZ23syflhhzZ.localdomain' 修改访问地址:external_url 'https://git.studyjava.cn' sudo gitlab-ctl reconfigure ### 配置nginx的https nginx 配置文件 cd /var/opt/gitlab/ 启用https 只需要修改nginx为https就行 在 /etc/gitlab/ssl 上传 和你域名一样的key和crt 例如git.studyjava.cn.crt git.studyjava.cn.key 启动nginx等服务 cd /opt/gitlab/init ./nginx start | stop ### 完成 通过浏览器访问GitLab,下面是默认的账号和密码 Username: root Password: 5iveL!fe ### 常见问题 1.配置了如上还是不能发送邮件 ``` #var/opt/gitlab/.gitconfig修改user [user] name = GitLab # email = gitlab@git.studyjava.cn email = root@iZ23syflhhzZ.localdomain [core] autocrlf = input ```
作者:じ☆ve宝贝
MySQL 5.7以上版本,如果使用group by 去重可能会出现 this is incompatible with sql_mode=only_full_group_by,其实是如果有些值并非一行,不会默认取第一行,因此会发生此异常! ``` Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by ``` 解决办法: ``` SELECT any_value(p.id) id,any_value(p.name) NAME,…… FROM wm_plate p GROUP BY …… ```
作者:じ☆ve宝贝
 ## 一、问题: 数据库表数据量极大(千万条),要求让服务器更加快速地响应用户的需求。 ## 二、解决方案: 1.通过高速服务器Cache缓存数据库数据 2.内存数据库 (这里仅从数据缓存方面考虑,当然,后期可以采用Hadoop+HBase+Hive等分布式存储分析平台) ## 三、主流解Cache和数据库对比:  上述技术基本上代表了当今在数据存储方面所有的实现方案,其中主要涉及到了普通关系型数据库(MySQL/PostgreSQL),NoSQL数据库(MongoDB),内存数据库(Redis),内存Cache(Memcached),我们现在需要的是对大数据表仍保持高效的查询速度,普通关系型数据库是无法满足的。而MongoDB其实只是一种非关系型数据库,其优势在于可以存储海量数据,具备强大的查询功能,因此不宜用于缓存数据的场景。 从以上各数据可知,对于我们产品最可行的技术方案有两种: 1.**Memcached** 内存Key-Value Cache 2.**Redis** 内存数据库 #### 四、下面重点分析Memcached和Redis两种方案: ###### 4.1 Memcached介绍 Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提供动态、数据库驱动网站的速度,现在已被LiveJournal、hatena、Facebook、Vox、LiveJournal等公司所使用。 ###### 4.2 Memcached工作方式分析 许多Web应用都将数据保存到 RDBMS中,应用服务器从中读取数据并在浏览器中显示。 但随着数据量的增大、访问的集中,就会出现RDBMS的负担加重、数据库响应恶化、 网站显示延迟等重大影响。Memcached是高性能的分布式内存缓存服务器,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web等应用的速度、 提高可扩展性。下图展示了memcache与数据库端协同工作情况:  其中的过程是这样的: 1.检查用户请求的数据是缓存中是否有存在,如果有存在的话,只需要直接把请求的数据返回,无需查询数据库。 2.如果请求的数据在缓存中找不到,这时候再去查询数据库。返回请求数据的同时,把数据存储到缓存中一份。 3.保持缓存的“新鲜性”,每当数据发生变化的时候(比如,数据有被修改,或被删除的情况下),要同步的更新缓存信息,确保用户不会在缓存取到旧的数据。 Memcached作为高速运行的分布式缓存服务器,具有以下的特点: 协议简单 基于libevent的事件处理 内置内存存储方式 memcached不互相通信的分布式 ###### 4.3 如何实现分布式可拓展性? Memcached的分布式不是在服务器端实现的,而是在客户端应用中实现的,即通过内置算法制定目标数据的节点,如下图所示:  ###### 4.4 Redis 介绍 Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、 list(链表)、set(集合)和zset(有序集合)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步,当前 Redis的应用已经非常广泛,国内像新浪、淘宝,国外像 Flickr、Github等均在使用Redis的缓存服务。 ###### 4.5 Redis 工作方式分析 Redis作为一个高性能的key-value数据库具有以下特征: 多样的数据模型 持久化 主从同步 Redis支持丰富的数据类型,最为常用的数据类型主要由五种:String、Hash、List、Set和Sorted Set。Redis通常将数据存储于内存中,或被配置为使用虚拟内存。Redis有一个很重要的特点就是它可以实现持久化数据,通过两种方式可以实现数据持久化:使用RDB快照的方式,将内存中的数据不断写入磁盘;或使用类似MySQL的AOF日志方式,记录每次更新的日志。前者性能较高,但是可能会引起一定程度的数据丢失;后者相反。 Redis支持将数据同步到多台从数据库上,这种特性对提高读取性能非常有益。 ###### 4.6 Redis如何实现分布式可拓展性? 2.8以前的版本:与Memcached一致,可以在客户端实现,也可以使用代理,twitter已开发出用于Redis和Memcached的代理Twemproxy 。 3.0 以后的版本:相较于Memcached只能采用客户端实现分布式存储,Redis则在服务器端构建分布式存储。Redis Cluster是一个实现了分布式且允许单点故障的Redis高级版本,它没有中心节点,各个节点地位一致,具有线性可伸缩的功能。如图给出Redis Cluster的分布式存储架构,其中节点与节点之间通过二进制协议进行通信,节点与客户端之间通过ascii协议进行通信。在数据的放置策略上,Redis Cluster将整个 key的数值域分成16384个哈希槽,每个节点上可以存储一个或多个哈希槽,也就是说当前Redis Cluster支持的最大节点数就是16384。  ## 五、综合结论 应该说Memcached和Redis都能很好的满足解决我们的问题,它们性能都很高,总的来说,可以把Redis理解为是对Memcached的拓展,是更加重量级的实现,提供了更多更强大的功能。具体来说: ###### 1.性能上: 性能上都很出色,具体到细节,由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比 Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起 Memcached,还是稍有逊色。 ###### 2.内存空间和数据量大小: MemCached可以修改最大内存,采用LRU算法。Redis增加了VM的特性,突破了物理内存的限制。 ###### 3.操作便利上: MemCached数据结构单一,仅用来缓存数据,而Redis支持更加丰富的数据类型,也可以在服务器端直接对数据进行丰富的操作,这样可以减少网络IO次数和数据体积。 ###### 4.可靠性上: MemCached不支持数据持久化,断电或重启后数据消失,但其稳定性是有保证的。Redis支持数据持久化和数据恢复,允许单点故障,但是同时也会付出性能的代价。 ###### 5.应用场景: Memcached:动态系统中减轻数据库负载,提升性能;做缓存,适合多读少写,大数据量的情况(如人人网大量查询用户信息、好友信息、文章信息等)。 Redis:适用于对读写效率要求都很高,数据处理业务复杂和对安全性要求较高的系统(如新浪微博的计数和微博发布部分系统,对数据安全性、读写要求都很高)。 ## 六、需要慎重考虑的部分 1.Memcached单个key-value大小有限,一个value最大只支持1MB,而Redis最大支持512MB 2.Memcached只是个内存缓存,对可靠性无要求;而Redis更倾向于内存数据库,因此对对可靠性方面要求比较高 3.从本质上讲,Memcached只是一个单一key-value内存Cache;而Redis则是一个数据结构内存数据库,支持五种数据类型,因此Redis除单纯缓存作用外,还可以处理一些简单的逻辑运算,Redis不仅可以缓存,而且还可以作为数据库用 4.新版本(3.0)的Redis是指集群分布式,也就是说集群本身均衡客户端请求,各个节点可以交流,可拓展行、可维护性更强大。