<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>迷途书童</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://blog.yangricky.com/</id>
  <link href="https://blog.yangricky.com/" rel="alternate"/>
  <link href="https://blog.yangricky.com/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, 迷途书童</rights>
  <subtitle>学习 / 职业 / 生活</subtitle>
  <title>迷途书童</title>
  <updated>2026-05-10T01:21:01.692Z</updated>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="认知" scheme="https://blog.yangricky.com/categories/%E8%AE%A4%E7%9F%A5/"/>
    <category term="思维" scheme="https://blog.yangricky.com/tags/%E6%80%9D%E7%BB%B4/"/>
    <category term="认知" scheme="https://blog.yangricky.com/tags/%E8%AE%A4%E7%9F%A5/"/>
    <category term="哲学" scheme="https://blog.yangricky.com/tags/%E5%93%B2%E5%AD%A6/"/>
    <content>
      <![CDATA[<p><img src="/images/hedgehog1.png"></p><p>狐狸走在林子里，遇见了刺猬。</p><p>它从早到晚围着刺猬的巢穴转，等待最佳时机。皮毛光滑，脚步飞快，阴险狡猾，看上去准是赢家。</p><p>刺猬毫不起眼，走路一摇一摆，整天只想着自己的事。等狐狸扑上来，它立刻蜷缩成一个圆球，浑身的尖刺指向四面八方。狐狸知道这是毫无意义的进攻，悻悻撤回了森林。</p><p>这是古希腊诗人阿尔洛克斯写下的一则寓言，大约是公元前7世纪的事。它流传了将近三千年，最终落进了以赛亚·伯林的手里。</p><p>伯林用这则寓言做了一个至今仍被反复引用的比喻：思想家，乃至所有人，都可以分成两种类型。<strong>狐狸知道很多事，而刺猬只知道一件大事。</strong></p><span id="more"></span><h2 id="刺猬的力量"><a href="#刺猬的力量" class="headerlink" title="刺猬的力量"></a>刺猬的力量</h2><p>刺猬知道一件大事。</p><p>认定一件事，然后把所有的精力往那一个点上使。他们最厉害的地方，不是知道该做什么，而是知道不该做什么。</p><p>巴菲特的投资原则极其简单：只投自己看得懂的生意。科技股起飞的年代，他不碰；互联网泡沫，他不碰；加密货币狂潮，他还是不碰。每次被人嘲笑落伍，每次沉默，每次依然我行我素。他的原则不是”我聪明”，而是”我知道自己的边界”。结果是：活过了所有的泡沫，活成了传奇。</p><p>亚马逊的贝佐斯用了同一种方法。他有一个核心理念，20年没变过：<strong>一切以客户体验为核心</strong>。无论是物流、云计算还是Prime会员，所有的产品决策，最终都要回答同一个问题：这对客户有没有更好？其他公司在追竞争对手、追季报、追行业风口，贝佐斯只追那一件事。</p><p>乔布斯更是极端。他认定”至简”是一切产品设计的核心，为此不惜得罪工程师、客户乃至董事会。iPod发布前，他要求把整个播放器做进一个衬衫口袋。有工程师说做不到，他把一块石子扔进鱼缸，气泡冒出来，说：”有气泡，就有空间；有空间，就有办法。”最后做出来了。</p><p>Unix 也是同一种刺猬。1969年，Ken Thompson 和 Dennis Ritchie 在贝尔实验室设计 Unix 的时候，定下了一条核心哲学：<strong>每个程序只做一件事，并把它做好</strong>。<code>ls</code> 只列文件，<code>grep</code> 只搜文本，<code>cat</code> 只输出内容，程序之间用管道连接，各司其职。这条原则极其简单，极其克制，也极其难以坚守——因为软件工程师最大的冲动就是往一个东西里塞更多功能。但 Unix 守住了。五十年过去，Linux、macOS、Android，半个计算机世界都建在这个哲学上面。</p><p>刺猬的力量，在于<strong>聚焦</strong>。一旦认定了核心理念，所有的精力都可以往一个点上使，久而久之，能量密度极高。他们不为细节所动，不因反馈而动摇——这有时候是固执，有时候是坚守。</p><p>你大概也在职场里遇到过这样的人：开什么会，他最终都会把话题绕回同一个判断；面对什么新方案，他都有自己的一把尺子去量。一开始觉得这人怎么这么一根筋，后来发现——他是对的，而且他能做出别人做不出的事。</p><h2 id="刺猬的陷阱"><a href="#刺猬的陷阱" class="headerlink" title="刺猬的陷阱"></a>刺猬的陷阱</h2><p>但简单原则落到现实世界里，马上遭遇悖论。本质原因只有一个：<strong>任何原则都有它的前提条件和适用边界。</strong> 刺猬的陷阱，就是忘记了这件事。</p><p>桥水资本创始人瑞·达里欧有一本书叫《原则》，里面提到他公司的核心管理原则是”讲真话”——要求包括他自己在内的所有人极端诚实，对人对事有什么意见一定要当面说出来。听起来挺对吧？我们都想生活在这样的世界里。</p><p>但你想一想，现实中真的能这样吗？</p><p>你觉得某个同事的技术很烂，你能直接说吗？大概率不能——至少不能当着所有人的面，在周会上，用那种语气说。就算你说的是事实，就算你的出发点是好的，场合不对、措辞不对，结果就是你得罪了人，气氛僵了，事情反而更难推进。</p><p>所以”讲真话”这个原则，其实是不可能彻底贯彻的。它落地的时候，一定变成了另一套东西：什么真话可以讲，什么场合可以讲，可以讲到什么程度，什么时候必须闭嘴。这套东西，比”讲真话”三个字复杂得多。如果你只掌握了那个简洁的原则，在任何一个团队里都是生存不下去的。</p><p>还有一个著名的段子：有人问神父，”祈祷的时候我可以抽烟吗？”神父说当然不可以。又有人问，”抽烟的时候我可以祈祷吗？”神父说当然可以。同一个原则，不同的问法，截然相反的结论。</p><p>原则之所以简洁，是因为它省略了太多。省略的那些，往往就是现实世界。</p><p>叔本华说过一句话：<strong>每个人都把自己视野的极限当成世界的极限。</strong></p><p>刺猬的问题不是核心理念是错的——很多时候它确实是对的——而是<strong>把有条件的正确当成了无条件的真理</strong>。</p><p>柯达是最典型的例子。聚焦胶片摄影，这个选择在很长一段时间里是完全正确的，柯达靠它统治了整个行业。更讽刺的是，1975年，柯达自己的工程师史蒂文·萨森发明了世界上第一台数码相机。但管理层看了看，觉得这东西会蚕食胶片业务，把它压了下去。他们的核心理念——胶片才是摄影的根本——在那一刻从战略变成了枷锁。等到数码时代真的来临，柯达已经无力转身。</p><p>柯达的问题不是当初选错了，而是<strong>忘记了问：这件事，还有多久是对的？</strong> 前提条件变了，边界移动了，原则却没有跟着更新。</p><p>对比之下，Netflix 的选择完全不同。Netflix 起家是 DVD 邮寄租碟，这件事它做到了极致。但 Reed Hastings 很早就看到了实体光盘的保质期，2007 年他主动把自己的 DVD 业务往死里打，all-in 流媒体——哪怕当时流媒体几乎还不存在。他们甚至一度想把 DVD 和流媒体拆成两家公司，后来又合并回来，整个过程一直在折腾、在更新。同期的百视达死守门店模式，2010 年破产。Netflix 今天市值超过 2000 亿美元。</p><p>两家公司，同样在影视租赁行业聚焦到了极致，区别只有一个：百视达认定的”一件大事”是门店和实体光盘，Netflix 认定的是”让人随时随地看到想看的内容”。前者是形式，后者是本质——形式会过期，本质不会。</p><h2 id="狐狸的灵活"><a href="#狐狸的灵活" class="headerlink" title="狐狸的灵活"></a>狐狸的灵活</h2><p>狐狸知道很多事。</p><p>它是现实主义者，心里没有宏大叙事，也不急于找到终极答案。兵来将挡，水来土掩，走一步看一步，根据反馈决定下一步。这是”以万变应万变”的生存策略。</p><p>互联网时代，狐狸式的思维模式被发展成了方法论：”小步快跑，快速迭代”，”MVP（最小可行产品）”，”数据驱动决策”。这些词今天已经成了职场常用词，背后的逻辑是一致的：不要在未经验证的假设上押大注，先跑起来，从世界的反馈中学习，边走边修正。</p><p>狐狸的学习，来自三种反馈：</p><ul><li><strong>得失</strong>：赢了继续，输了改变。</li><li><strong>榜样</strong>：跟榜样一样就继续，不一样就调整。</li><li><strong>环境</strong>：适应就存活，不适应就淘汰。</li></ul><p>这三种反馈，本质都是<strong>适应性学习</strong>。放到合适的场景里，威力巨大。</p><p>Instagram 是最干净的例子。Kevin Systrom 最初做的是一个叫 Burbn 的签到应用，功能很多，方向也挺清晰。但用户数据显示——大家主要在用里面的拍照分享功能，其他功能几乎没人碰。他没有坚持原来的产品方向，而是把 Burbn 几乎推倒重来，只留下照片分享，改名 Instagram 重新上线。18 个月后，Facebook 以 10 亿美元收购。</p><p>Slack 的故事更有意思。Stewart Butterfield 的团队在做一款叫 Glitch 的网络游戏，做了好几年，最终失败了。但他们开发过程中顺手搭了一个内部沟通工具用来协调团队。游戏关掉之后，他们发现这个沟通工具比游戏本身更有价值，于是把它做成了产品——就是今天的 Slack。2021 年，Salesforce 以 277 亿美元收购。</p><p>两个例子说的是同一件事：<strong>世界的反馈，比你的预设更诚实。</strong> 狐狸的本事，是真的听进去了。</p><h2 id="狐狸的短视"><a href="#狐狸的短视" class="headerlink" title="狐狸的短视"></a>狐狸的短视</h2><p>但狐狸有一个致命的弱点：<strong>所有的适应性学习，都是短视的。</strong></p><p>无论反馈来自成败、榜样还是环境，它给你的都只是局部的、当下的信号。你根据这个信号做出的调整，也自然是局部的、短暂的。</p><p>上市公司是最典型的狐狸陷阱。管理者为了股价好看，逼着自己每一年、甚至每个季度都要保持利润增长，于是拼命压成本，大量裁员，把利润做漂亮——每一步都很好地响应了市场的短期反馈，但压掉的，可能正是公司的长期竞争力。每一步都满足，最后落得没有未来。</p><p>诺基亚是另一个版本。鼎盛时期，诺基亚占据全球手机市场近 40% 的份额，产品迭代极快，用户调研做得极细，是典型的狐狸式运营。但也正因为太擅长这套打法，太适应功能机的游戏规则，当苹果用 iPhone 重新定义了手机是什么的时候，诺基亚的反应慢了半拍。他们不是不知道触屏和智能化的趋势，内部早有相关原型——但每一次市场调研反馈的都是”用户还是要实体键盘”，每一次短期数据都在说”功能机还在卖”。他们太忠实于反馈了，反而被反馈困住了。</p><p>百度是另一个极端——<strong>什么都追，什么都没走深</strong>。搜索起家，但没有守住这个根基；之后陆续进入地图、云计算、智能音箱（小度）、自动驾驶（Apollo）、医疗健康、视频（爱奇艺）……几乎每一个科技风口都有百度的影子，每一次都是在响应当时最热的市场信号。但字节跳动用推荐算法抢走了用户注意力，微信用社交关系抢走了流量入口，AI 大模型时代百度又慢了一步。追了这么多风口，核心的搜索广告业务却持续承压。反观同期的 Google，始终把搜索和广告系统做到极深，其他扩张都围绕这个核心展开，市值一路稳居全球前列。</p><p>更触目惊心的例子，是二战前的日本。从明治维新，到甲午战争，到日俄战争，日本几乎赢得了他们打的每一仗。每一次胜利都强化了他们的战略和作战方式，他们越来越擅长这套打法，越来越适应这个游戏规则。但等到他们把自己的环境撑满，再往前走一步，偷袭珍珠港，惹翻了美国人，环境发生了巨变——接下来是毁灭性的灾难。</p><p>用一句话概括：<strong>日本赢得了每一个战术胜利，终于一步步地把自己带进了战略陷阱。</strong></p><p>道理很简单，却反直觉——你如果赢得每一局，就意味着你变得越来越适应当前的环境，你的行为策略越来越稳定，整个环境都被你的逻辑撑满了。如果环境不变，你就是王。但要命的是，这个时代，环境不仅在变，而且越变越快。一旦剧变，你拿什么去对付变化？</p><p>反馈是真实的，但反馈只是局部的现实。狐狸的短视，不是因为它笨，恰恰相反——正因为它聪明，善于从反馈中学习，才会不自觉地把自己锁死在当下的游戏规则里，浑然不觉。</p><p><img src="/images/hedgehog2.png"></p><h2 id="伯林本人：一只以为自己是狐狸的刺猬"><a href="#伯林本人：一只以为自己是狐狸的刺猬" class="headerlink" title="伯林本人：一只以为自己是狐狸的刺猬"></a>伯林本人：一只以为自己是狐狸的刺猬</h2><p>说到这里，有一个细节非常耐人寻味。</p><p>以赛亚·伯林提出了刺猬与狐狸的比喻，他说自己是一只典型的狐狸。他的研究确实涉及许多领域——政治哲学、思想史、音乐、文学，兴趣极为广泛。他写过赫尔岑，写过马基雅维利，写过维科，写过赫尔德，这些人在思想史上天南海北，互不隶属。</p><p>但果真如此吗？</p><p>仔细看他一生的工作，你会发现：伯林其实也有刺猬的一面。他一辈子都在追问同一个问题——<strong>为什么政治实践，原本出于非常美好的愿望，却在某些观念的指导下，造成了灾难性的后果？</strong></p><p>这个问题，来自他8岁时目睹的那次私刑，来自他在俄国度过的童年，来自他作为犹太人对流离感的切身体验，来自他对两次大革命的长期反思。他的”两种自由”理论，他的”价值多元主义”，本质上都是在从不同角度回答同一个问题。</p><p>所以伯林既是狐狸，也是刺猬——他用狐狸的广博视野，服务于刺猬的那件大事。</p><p>这反而是这个比喻最有意思的地方：<strong>两种类型的边界，并没有我们以为的那么清晰。</strong></p><p>万维钢有一句话我觉得说得很准：刺猬和狐狸的差异，说的不是技能的专一或广博，说的是一个人对<strong>知识的态度</strong>。王安石多才多艺，会写诗还会考证古文字，但他做事一根筋，听不进意见，所以他是刺猬。艾森豪威尔没什么特别深厚的专业知识，但做事不极端，灵活掌握中庸之道，他是狐狸。</p><p>真正的刺猬，不是专业知识窄的人，而是<strong>心里只认一件事的人</strong>。<br>真正的狐狸，不是什么都懂的人，而是<strong>永远保持开放、愿意被现实修正的人</strong>。</p><p>知识的宽窄是技能问题，需要时间和训练；而对知识的态度，却是一种选择。</p><h2 id="那么，怎么办？"><a href="#那么，怎么办？" class="headerlink" title="那么，怎么办？"></a>那么，怎么办？</h2><p>那到底应该当刺猬还是狐狸？</p><p>这个问题本身，可能就是一个刺猬式的提问——把一件复杂的事情，压缩成了一个非此即彼的选择。</p><p>FT 中文网前主编王烁有一个妙论，值得仔细品。他说：<strong>当狐狸，但是搭刺猬的便车。</strong></p><p><img src="/images/hedgehog3.png"></p><p>具体来说，是三件事：</p><p><strong>第一，我们自己要是一只狐狸。</strong> 现实主义者，敏锐感知环境的反馈，不断调整自己，随时更新工具。不要在一个快速变化的世界里死守一个不再适用的原则。互联网时代每隔几年就要经历一次范式转移，一个十年前有效的策略，今天可能已经成了负担。</p><p><strong>第二，对这个世界上的刺猬好一点。</strong> 刺猬本身的下场也许会很惨——极端的原则主义者往往在现实世界里碰得头破血流。但他们有一项独特的本事：<strong>着眼于长期，总是会给我们发来长期主义的信号</strong>。在一个所有人都追着短期反馈跑的世界里，这是最稀缺的东西。刺猬的存在，给了狐狸们一个参照——提醒我们，除了当下的游戏，还有另一个维度的事情值得在意。</p><p><strong>第三，听刺猬说的话，但小心被他们带进沟里。</strong> 刺猬给出的是方向感，不是导航仪。方向感可以在你完全迷失的时候给你一根柱子，但如果你把它当成地图来用，你会走到悬崖边上还以为自己走在正路上。</p><p>这三件事，不是一个公式，而是一种张力——狐狸与刺猬之间，需要始终保持的张力。</p><h2 id="个人的一点想法"><a href="#个人的一点想法" class="headerlink" title="个人的一点想法"></a>个人的一点想法</h2><p>我常常觉得，这个比喻真正的价值，不在于问”你是哪种人”，而在于提醒我们：<strong>认知策略，需要情境依赖。</strong></p><p>创业初期，要当狐狸。快速试错，不要把早期的假设当成信仰。等到找到了真正的核心之后，要有刺猬的坚守——不要因为每一次短期反馈就动摇了对长期方向的判断。</p><p>做技术选型，可以当狐狸，广泛调研，多方对比，不迷信任何一种技术宗教。但选定之后，在一段时间内要有刺猬的专注，深入进去，理解它的边界，而不是永远在”要不要换个方案”的焦虑里打转。</p><p>更重要的是：<strong>当你不确定一件事的时候，狐狸的开放性是更安全的姿势；当你已经理解了一件事的核心逻辑之后，刺猬的坚守才是力量的来源。</strong> 弄反了，就会出问题。用刺猬的教条去处理你其实还没摸透的事情，是傲慢；用狐狸的开放性来回避你其实已经看清楚了的坚守，是逃避。</p><p>中年危机，其实很多时候就是刺猬和狐狸的账单同时到期。</p><p><img src="/images/hedgehog4.png"></p><p>一种人深耕了一个领域二十年，某天突然发现这个行业在萎缩，自己的经验开始贬值——这是刺猬的中年危机。IT 行业里有一类很典型的情况：在一家公司做了十年，技术栈从来没有更新过，Java 还是老版本，架构还是当年那套，新的云原生、大模型这些东西从来没碰过。不是不努力，而是在一个稳定的环境里把一套东西用得太熟了，熟到没有动力再往外看一眼。等到某天公司裁员、或者想换工作，才发现市场上招的人要的东西和自己会的完全对不上。问题不是他不够努力，而是他认定的”一件大事”是一个具体的技术形式，而不是背后可以迁移的工程能力。这种情况下，该问的问题是：我这些年真正练出来的是什么？系统设计的思维、解决复杂问题的能力——这些在新的技术栈里还能用。但如果连这些也没有，只是熟悉了一套工具的操作，那才是真正的危险。</p><p>另一种人换了很多工作，见识很广，却在某天意识到自己好像没有一件事是真正走深过的——这是狐狸的中年危机。IT 行业里同样常见：后端做过、前端碰过、产品摸过、管理也试过，每一次跳槽都是响应当时的市场信号，哪里薪资高去哪里，哪个方向热门去哪里。每一步看起来都是”理性的”，但十年积累下来，没有一件事真正走到过深处，简历写得很长，却很难说清楚自己到底擅长什么。这种情况下，该问的问题是：我在哪件事上，愿意从现在开始当一次刺猬？</p><p>伯林最终给了我们一个很难的答案：这个世界是多元的，价值与价值之间存在真实的冲突，没有一种理念能完全解决所有问题。这不是一个令人振奋的结论，它意味着我们永远活在取舍之中，永远不能得到一个完美的答案。</p><p>但他同时告诉我们：承认这个复杂性，比把它简化掉，要诚实得多，也有用得多。</p><p>其实，把刺猬和狐狸的问题抽象到更高一层，背后藏着三个字：<strong>适度、适应、适合</strong>。适度——不走极端，刺猬和狐狸都不能偏到底；适应——保持对变化的感知，不要让原则过了保质期还在用；适合——没有放之四海而皆准的模式，只有在对的场景用对的方式。这三件事，说起来简单，真正做到却需要一辈子的练习。如果你感兴趣，我在<a href="/2020/08/29/three-principles/">三适原则</a>里对这个框架有更完整的展开。</p>]]>
    </content>
    <id>https://blog.yangricky.com/2026/05/08/hedgehog-and-fox/</id>
    <link href="https://blog.yangricky.com/2026/05/08/hedgehog-and-fox/"/>
    <published>2026-05-08T04:45:00.000Z</published>
    <summary>
      <![CDATA[<p><img src="/images/hedgehog1.png"></p>
<p>狐狸走在林子里，遇见了刺猬。</p>
<p>它从早到晚围着刺猬的巢穴转，等待最佳时机。皮毛光滑，脚步飞快，阴险狡猾，看上去准是赢家。</p>
<p>刺猬毫不起眼，走路一摇一摆，整天只想着自己的事。等狐狸扑上来，它立刻蜷缩成一个圆球，浑身的尖刺指向四面八方。狐狸知道这是毫无意义的进攻，悻悻撤回了森林。</p>
<p>这是古希腊诗人阿尔洛克斯写下的一则寓言，大约是公元前7世纪的事。它流传了将近三千年，最终落进了以赛亚·伯林的手里。</p>
<p>伯林用这则寓言做了一个至今仍被反复引用的比喻：思想家，乃至所有人，都可以分成两种类型。<strong>狐狸知道很多事，而刺猬只知道一件大事。</strong></p>]]>
    </summary>
    <title>刺猬死于专注，狐狸死于灵活</title>
    <updated>2026-05-10T01:21:01.692Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="认知" scheme="https://blog.yangricky.com/categories/%E8%AE%A4%E7%9F%A5/"/>
    <category term="思维" scheme="https://blog.yangricky.com/tags/%E6%80%9D%E7%BB%B4/"/>
    <category term="决策" scheme="https://blog.yangricky.com/tags/%E5%86%B3%E7%AD%96/"/>
    <category term="经济学" scheme="https://blog.yangricky.com/tags/%E7%BB%8F%E6%B5%8E%E5%AD%A6/"/>
    <content>
      <![CDATA[<p><img src="/images/arrow-1.jpg"></p><p>同事中午出去吃饭。三个选项：米饭、面条、饺子。</p><p>同事甲：米饭 &gt; 面条 &gt; 饺子<br>同事乙：饺子 &gt; 米饭 &gt; 面条<br>同事丙：面条 &gt; 饺子 &gt; 米饭</p><p>每个人的偏好都是理性的——同事甲觉得米饭比面条好，面条比饺子好，所以米饭肯定比饺子好。这叫<strong>可传递性</strong>。同事乙和同事丙也一样。</p><span id="more"></span><p>如果直接三选一，一人一票，平局——谁也吃不上了。</p><p>于是有人说：「先把米饭和面条比一下，赢的再去和饺子比。」</p><ul><li>米饭 vs 面条 → 米饭赢（同事甲、同事乙选米饭，同事丙选面条）</li><li>米饭 vs 饺子 → 饺子赢（同事乙、同事丙选饺子，同事甲选米饭）</li></ul><p>→ 吃饺子。</p><p>但如果换个议程：「先把面条和饺子比。」</p><ul><li>面条 vs 饺子 → 面条赢（同事甲、同事丙选面条，同事乙选饺子）</li><li>面条 vs 米饭 → 米饭赢（同事甲、同事乙选米饭，同事丙选面条）</li></ul><p>→ 吃米饭。</p><p><strong>同样的三个人，同样的偏好。议程不同，结果不同。</strong></p><p>而且每一轮大家投的都是真心的，没人骗人。问题不出在投票的人，出在投票怎么被组织。</p><p>米饭 &gt; 面条，面条 &gt; 饺子，饺子 &gt; 米饭。循环了。</p><p>三个理性的个体，拼出了一个非理性的集体。阿罗用数学证明：这不是偶然，是必然。</p><p>不过，下面四种情况不在阿罗不可能定理的讨论范围之内：</p><ol><li><strong>只有一个人：</strong> 不需要投票，也就不存在集体决策的问题。阿罗研究的是「一群人的偏好怎么加总」，一个人没有加总的对象。</li><li><strong>偏好一致：</strong> 如果三个同事都最喜欢米饭，那就直接吃了。不需要投票，也就不存在悖论。</li><li><strong>只有两个选项：</strong> 如果只有米饭和面条，两个选项只有一条线，A &gt; B 或 B &gt; A，不可能形成 A &gt; B &gt; A 的环。阿罗本人明确限定，定理讨论的是<strong>不少于三种方案</strong>。这也是为什么午饭例子里一旦把饺子加进来，事情才变得不对劲。</li><li><strong>用打分代替排序：</strong> 阿罗讨论的是排序式偏好——A &gt; B &gt; C，不说「好多少」。如果让每个人打分（米饭 5 分、面条 3 分、饺子 2 分），加总后取最高分，问题就变了。打分制可以避开循环，但这不是阿罗那个框架里的东西——它属于另一个讨论。</li></ol><p>这四种情况不在阿罗的讨论范围。但如果你要求一个投票规则同时满足下面四个条件——每一个单独看都合理得像废话——阿罗证明了：<strong>不可能。</strong></p><h2 id="四个听起来像废话的条件"><a href="#四个听起来像废话的条件" class="headerlink" title="四个听起来像废话的条件"></a>四个听起来像废话的条件</h2><p>阿罗说，一个理想的集体决策规则，应该同时满足四个条件：</p><ol><li><strong>帕累托法则：</strong> 如果所有人都认为 A 比 B 好，那集体就应该选 A。但现实中，全体一致太难了——三个人里有一个不说话，你就不知道他是同意还是懒得争。</li><li><strong>无限制定义域：</strong> 不能限制任何人的偏好。你喜欢饺子？可以。但现实中，总有人说「饺子不算正餐」「这个选项不讨论」——偏好还没被投，先被过滤了。</li><li><strong>无关备选项的独立性：</strong> 在米饭和面条之间选的时候，饺子不应该干扰结果。但现实中，选项一多，人就会变——本来坚定选米饭，听说还有饺子，突然觉得面条也不错。</li><li><strong>非独裁：</strong> 不能有一个人说了就算。但现实的情况往往就是：老板的权力大，往往代替员工做选择，员工的意见往往不重要</li></ol><p><img src="/images/arrow-2.jpg"></p><h2 id="所以民主没用了吗？"><a href="#所以民主没用了吗？" class="headerlink" title="所以民主没用了吗？"></a>所以民主没用了吗？</h2><p>总有人把阿罗不可能定理当成「民主无效」的证据。不是的。</p><p>「不可能」说的是——在极其苛刻的条件下，找不到完美的投票规则。不是说所有投票都没意义。</p><p>阿罗插了一个路标：<strong>此路不通</strong>。此路不通，可以绕。后来的诺奖得主森、马斯金，都是通过放松阿罗的前提条件（比如限制某些冷僻排序），找到了绕路的方法。</p><p>其实，阿罗不可能定理更多是一种「边界提醒」。它不是说投票没意义，而是说投票有一个隐藏的前提：<strong>大家得先在目的上达成一致。</strong></p><p>回到午饭例子。如果三个同事的目标都是「吃饱、省钱、快点吃完」，那选米饭还是面条只是手段之争——可以投票，可以妥协，最终吃什么都行。但如果同事甲的目标是省钱，同事乙的目标是吃爽，同事丙的目标是吃健康——那阿罗不可能定理就不适用了。目标不一样，投票解决不了，这不是数学问题，是前提没对齐。</p><p><img src="/images/arrow-3.jpg"></p><h2 id="阿罗这个人"><a href="#阿罗这个人" class="headerlink" title="阿罗这个人"></a>阿罗这个人</h2><p>1921 年生于纽约，犹太移民后代。51 岁拿诺贝尔奖，是当时最年轻的诺奖经济学得主。</p><p>他证明了「一般均衡」——亚当·斯密靠直觉提出的「看不见的手」，被阿罗用数学放在了稳固的地基上。经济学界对他几乎有一个共识：阿罗的研究，可以折成好几个诺奖。</p><p>有一个细节。朋友想测试他是不是真的什么都知道，让他去参加一个聚会。阿罗推开门，发现满屋子的人都在讨论<strong>企鹅</strong>——一个冷门到不能再冷门的领域。他想都没想，马上发表了一篇演讲，专业程度折服了一屋子企鹅学家。</p><p>但他的成名作——不可能定理——最初的动机不是政治，是钱。</p><p>阿罗研究厂商理论时发现：厂商不是一个人，股东有好几个。每个股东都想利润最大化，但每个人主张的方案不一样——扩产、压成本、加研发投入，各有各的道理。厂商在做生产决策时，本质上是一次投票。怎么投？</p><p>他一头钻进去，捣鼓出了不可能定理。发表之后，才有读者告诉他：法国人孔多塞几百年前就想过类似的问题。</p><p>阿罗数学极好，但当时读书不多。书读得不多的人，有时候反而能绕开前人的设限，看到别人看不到的东西。后来他成了通才，什么书都读——企鹅都能讲。</p><h2 id="不只是民主投票的问题"><a href="#不只是民主投票的问题" class="headerlink" title="不只是民主投票的问题"></a>不只是民主投票的问题</h2><p><strong>个体理性，加起来未必集体理性。</strong></p><p>这不只是政治学的事。你想想：</p><ul><li>三个产品经理投票决定功能优先级。</li><li>五个投资人投票决定投哪家公司。</li><li>一个团队用「少数服从多数」决定技术方案。</li></ul><p>只要有三个人、三个选项，就有可能踩进同一个坑：投票结果不一定取决于每个人的偏好，而取决于谁决定了议程。</p>]]>
    </content>
    <id>https://blog.yangricky.com/2026/05/04/arrow-impossibility-theorem/</id>
    <link href="https://blog.yangricky.com/2026/05/04/arrow-impossibility-theorem/"/>
    <published>2026-05-04T00:00:00.000Z</published>
    <summary>
      <![CDATA[<p><img src="/images/arrow-1.jpg"></p>
<p>同事中午出去吃饭。三个选项：米饭、面条、饺子。</p>
<p>同事甲：米饭 &gt; 面条 &gt; 饺子<br>同事乙：饺子 &gt; 米饭 &gt; 面条<br>同事丙：面条 &gt; 饺子 &gt; 米饭</p>
<p>每个人的偏好都是理性的——同事甲觉得米饭比面条好，面条比饺子好，所以米饭肯定比饺子好。这叫<strong>可传递性</strong>。同事乙和同事丙也一样。</p>]]>
    </summary>
    <title>阿罗不可能定理：为什么三个理性的人，凑在一起会变得不理性？</title>
    <updated>2026-05-10T01:24:54.835Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="Spring" scheme="https://blog.yangricky.com/categories/Spring/"/>
    <category term="Spring" scheme="https://blog.yangricky.com/tags/Spring/"/>
    <category term="源码" scheme="https://blog.yangricky.com/tags/%E6%BA%90%E7%A0%81/"/>
    <content>
      <![CDATA[<p>对于一个系统的使用者来说，源码就是一个黑盒，不需要关心太多. 正如陀螺仪之于手表，集成电路之于CPU一样，仅仅需要了解这个系统可以做什么，以及这个系统不可以做什么就可以了.</p><ul><li>背景</li><li>IOC</li><li>DI</li></ul><span id="more"></span><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><h3 id="为什么需要读源码？"><a href="#为什么需要读源码？" class="headerlink" title="为什么需要读源码？"></a>为什么需要读源码？</h3><p>  搞清楚目的和动机，往往比做什么和怎么做更重要.<br>  先说结论</p><ul><li>对于我们大多数人来说，不需要读源码或者说读了没什么用. 换言之，我们在读任何源码之前，要问一个问题，为什么要读它?</li></ul><p>  如果我们需要读源码，基本上就是下面两个大的理由</p><ul><li>为了源码而源码<ul><li>因为别人看了，我们也要看</li><li>为了装X或面试</li><li>….</li></ul></li><li>为了解决某个问题<ul><li>代码运行的时候遇到报错，需要跟踪源码，这种情况很少见，如果出现这种情况，只能说明这个框架是做的还是不够好.</li><li>吸取源码的一些思想和设计思路，应用到自己的项目中</li></ul></li></ul><h3 id="读框架源码的基本策略"><a href="#读框架源码的基本策略" class="headerlink" title="读框架源码的基本策略"></a>读框架源码的基本策略</h3><ul><li>将框架拆解成模块，找到最核心的几个功能或者模块</li><li>聚焦一个模块，两种角度<ul><li>使用者(黑盒)角度<ul><li>需要了解哪些最少必要概念</li><li>引入哪些包</li><li>需要配置哪些参数</li><li>在代码层面需要添加什么东西</li><li>别的框架中是否有类似的模块？</li></ul></li><li>开发者(白盒)角度<ul><li>需要了解哪些最少必要概念</li><li>设计原则，模式</li><li>模块的workflow是什么，牵涉到时序图，类图，流程图等，这些图都是从不同的角度来描述这个模块.</li><li>与别的框架相比，模块有什么相同和不同</li></ul></li></ul></li></ul><h3 id="读源码和读书的类似"><a href="#读源码和读书的类似" class="headerlink" title="读源码和读书的类似"></a>读源码和读书的类似</h3><p> 读源码和读一本武侠小说或者宏观经济学没有大的区别，因为源码和书本质上都是信息，读源码就是萃取信息的过程。<br> 不同的阅读目的，需要用不同的姿势。这里的目的就预设为：吸取源码的一些思想和设计思路，应用到自己的项目中</p><ul><li><p>小说的主题和源码的初衷<br> 一本书肯定有一个或者某几个主题，比如歌颂爱情，追求真理。 框架的源码也是的，就Spring来说是为了简化Java开发 - 说起来是简单的，但实现起来并不简单。</p></li><li><p>小说的线索和源码的设计原则和策略<br> 就拿天龙八部来说，它的主线可以是侠义，恩仇，爱情。然后爱情这块就分为三个主人公的分线。Spring源码也可以认为有几条主线:IOC，AOP，POJO, 模版. 这些是Spring的起点和基石，贯穿了Spring的方方面面.</p></li><li><p>小说的结构和源码的的模块设计<br> 小说可以分为几个章节，每个章节大概讲了什么，每个章节的编排顺序是什么。同理，源码可以分为几个模块，模块之间的关系是什么？</p></li><li><p>小说的细节和代码的细节<br> 里面会有一些观点，然后为了证明这些观点会有事实和逻辑。同理，源码模块里的一些方法会有一些技术细节，比如if,else, try, catch, synchronized, 某个数据结构，或者某个事件的的触发. 所以在这里我们可以得出一个结论：如果读源码二话不说闷着头就跳进入到方法的细节里，这是不对的，是错误的方向.</p></li><li><p>看书读后感和看源码读后感<br>  小时候记得往往要写个什么观后感，现在想想这个事还是挺重要的。看一本书，可以看到作者传递的思想和价值主张(value proposal),得到某个启发，某个思维模型或框架，或者某个精彩的案例和论证,或者优雅的遣词造句，这些是对写作文有帮助的. 看源码也是，举个命名方面的例子，Spring在定位Bean的过程中涉及到AbstractRefreshableConfigAplicationContext类， 这个类名就很长，由5个单词组成, 一般而言，项目中很少取这么长名字来给类命名. 所以Spring的作者传递了这样一个思想：能清楚的表达设计意图是最重要的，其次才是长不长的问题。</p></li><li><p>心态<br>  如果用大口吃烤串的心态去看书和读源码，很显然是没有效果和收益的。现在大多数人比较浮躁，看东西讲究快和多，讲究快本身没有问题，但快了之后，质量就会下降。所以看经典框架的核心模块，要以喝茶的心态来看，也就是《思考，快与慢》里提到的”系统2”.</p></li><li><p>重要的的东西<br> 根据二八法则，一本书里只有20%的东西是重要的，看书不是越多越好，而是看少而精的书，那么源码也不例外，不需要阅读很多框架的源码，也不需要阅读一个框架的所有源码，那如何找到这20%？这是另外一个话题</p></li></ul><h2 id="IOC"><a href="#IOC" class="headerlink" title="IOC"></a>IOC</h2><h3 id="IOC想要讲的故事"><a href="#IOC想要讲的故事" class="headerlink" title="IOC想要讲的故事"></a>IOC想要讲的故事</h3><p> IOC - Inversion of Control, 中文是控制反转， 不管是中文还是英文，从字面上很难理解它的意思, 这与我随便说个词语”依赖变换“没什么区别。<br> 让我们切换一个角度，看看在代码层面IOC意味着什么. 现在有类A和类B，传统的做法是类A依赖于类B，IOC的做法是，A不要依赖于B, 要依赖于B的抽象AbstractB. 比较一下前后的变化</p><ul><li>A -&gt; 具体的B</li><li>A -&gt; AbstractB -&gt; (某种方式指向) 具体B</li></ul><p> 这种变化让人想到了什么？AbstractB像是一个中介，隔离了A和具体的B. AbstractB侧重描述做什么，具体的B侧重于描述怎么做。<br> 所以可以得出一个结论: IOC真正的内涵是将做什么(what)和怎么做(how)隔离开了, 可以认为<strong>做什么</strong> 是某个企业几十年不会变的核心概念和流程, <strong>怎么做</strong> 为了达成某件事的手段是多样的，现在世界本身也是如此，付款的方式有现金和手机支付。所以如果一个复杂的系统没有基于IOC, 那么这个系统没有未来，如果一个系统基于IOC，这个复杂的系统才有演化的可能.</p><p>对于IOC，我们有这样的肖像刻画</p><ul><li>将做什么和怎么做<strong>隔离</strong>出来了 - 总感觉做开发的天天无脑的念叨<strong>隔离</strong>就可以了….</li><li>符合依赖倒置原则</li><li>符合好莱坞原则 - 体现在对象实例化的过程中</li></ul><p>对于开发者来说，IOC在框架层面是标配，就好比汽车有个ABS系统一样，如果现在买辆车，如果没有防抱死系统，那真的是很奇怪.</p><h3 id="有哪些框架支持IOC功能？"><a href="#有哪些框架支持IOC功能？" class="headerlink" title="有哪些框架支持IOC功能？"></a>有哪些框架支持IOC功能？</h3><ul><li>Spring算一个 - Spring提供了很多功能，我们可以只使用其中的IOC功能</li><li>.net的Autofac</li><li>HK2 - 轻量级的IOC框架，</li><li>Guice - Google出品</li><li>如果没有上述框架，只能手写了</li></ul><h3 id="使用者的角度"><a href="#使用者的角度" class="headerlink" title="使用者的角度"></a>使用者的角度</h3><h4 id="一个简单的例子"><a href="#一个简单的例子" class="headerlink" title="一个简单的例子"></a>一个简单的例子</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建IOC容器</span></span><br><span class="line"><span class="type">ClassPathXmlApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;defaultconstruct/user.xml&quot;</span>);</span><br><span class="line"><span class="comment">// 实例化Bean,包含依赖注入的过程</span></span><br><span class="line"><span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> (User) context.getBean(<span class="string">&quot;user&quot;</span>);</span><br></pre></td></tr></table></figure><h4 id="有哪些框架支持IOC功能？-1"><a href="#有哪些框架支持IOC功能？-1" class="headerlink" title="有哪些框架支持IOC功能？"></a>有哪些框架支持IOC功能？</h4><ul><li>.net的Autofac</li><li>HK2 - 轻量级的IOC框架，</li><li>Guice - Google出品</li><li>如果没有上述框架，只能手写了</li></ul><h3 id="开发者的角度"><a href="#开发者的角度" class="headerlink" title="开发者的角度"></a>开发者的角度</h3><h4 id="Spring-IOC容器创建的大体过程（细节可见IOC时序图）"><a href="#Spring-IOC容器创建的大体过程（细节可见IOC时序图）" class="headerlink" title="Spring IOC容器创建的大体过程（细节可见IOC时序图）"></a>Spring IOC容器创建的大体过程（细节可见IOC时序图）</h4><ul><li>定位 - 设置Bean的路径，创建BeanFactory.</li><li>加载 - 读取BeanDefinition文件.</li><li>注册 - 解析BeanDefinition,并放入IOC容器当中.</li></ul><h4 id="核心方法"><a href="#核心方法" class="headerlink" title="核心方法"></a>核心方法</h4><ul><li><a href="https://github.com/yshua5631/spring-framework/blob/study/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java#L550-L638">refresh</a><ul><li><a href="https://github.com/yshua5631/spring-framework/blob/study/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java#L645-L682">prepareRefresh</a><ul><li><a href="https://github.com/yshua5631/spring-framework/blob/study/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java#L912-L978">preInstantiateSingletons</a><ul><li>getBean</li></ul></li></ul></li><li>obtainFreshBeanFactory<ul><li><a href="https://github.com/yshua5631/spring-framework/blob/study/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java#L122-L139">refreshBeanFactory</a></li><li><a href="https://github.com/yshua5631/spring-framework/blob/study/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java#L218-L227">customizeBeanFactory</a></li><li>loadBeanDefinitions</li></ul></li><li>prepareBeanFactory</li><li><a href="https://github.com/yshua5631/spring-framework/blob/study/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java#L929-L961">finishBeanFactoryInitialization</a></li></ul></li></ul><h4 id="IOC-时序图"><a href="#IOC-时序图" class="headerlink" title="IOC 时序图"></a>IOC 时序图</h4><p>  <img src="/images/spring/ioc.jpg" alt="avatar"></p><h3 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h3><h2 id="DI"><a href="#DI" class="headerlink" title="DI"></a>DI</h2><h3 id="DI和IOC的关系"><a href="#DI和IOC的关系" class="headerlink" title="DI和IOC的关系"></a>DI和IOC的关系</h3><p> IOC更多的是描述了做什么（要隔离规范和实现), DI是IOC的一种实现方式。 此外DL是IOC的另外一种实现方式</p><h3 id="使用者的角度-1"><a href="#使用者的角度-1" class="headerlink" title="使用者的角度"></a>使用者的角度</h3><h4 id="warmup-一个简单的依赖注入例子"><a href="#warmup-一个简单的依赖注入例子" class="headerlink" title="warmup: 一个简单的依赖注入例子"></a>warmup: 一个简单的依赖注入例子</h4><ul><li>定义Bean</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Primary</span> <span class="comment">/* 在装配的时候，可能会匹配多个Bean, Primary代表了这个Bean会被优先选择 */</span></span><br><span class="line">    <span class="meta">@Bean(name = &quot;redisTemplate&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> RedisTemplate&lt;String, Object&gt; <span class="title function_">redisTemplate</span><span class="params">(RedisConnectionFactory factory)</span> &#123;</span><br><span class="line">        RedisTemplate&lt;String, Object&gt; template = <span class="keyword">new</span> <span class="title class_">RedisTemplate</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">return</span> template;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>注入Bean</li></ul> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">AppRedisCacheManager</span> <span class="keyword">implements</span> <span class="title class_">IGlobalCache</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> RedisTemplate&lt;String, Object&gt; redisTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 注入RedisTemplate实例 */</span></span><br><span class="line">    <span class="comment">/* Autowired是Spring特有的注解，如果想让项目不局限于Spring可以使用Inject注解 */</span></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">AppRedisCacheManager</span><span class="params">(RedisTemplate redisTemplate)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.redisTemplate = redisTemplate;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="依赖注入的方式"><a href="#依赖注入的方式" class="headerlink" title="依赖注入的方式"></a>依赖注入的方式</h4><p>   上面的例子是典型的基于注解的自动注入。我看过不少介绍依赖注入的文章，有说有三种，有的说有五种. 按照我自己的理解，依赖注入有两大方式</p><ul><li>手动注入 - 必须显示的指定实例A的引用是B,一般是在XML里面定义或者JavaConfig里定义。</li><li>自动注入 - 不需要显示的指定实例A的引用是是B, 典型的形式是基于注解。 自动注入更加通用的内涵是：如果基于不同的条件，加载不同的Bean<ul><li>有哪些注解可以加载Bean?<ul><li>@Autowired - Spring特有的注解，默认按类型查找，如果找不到，按照名字查找.<ul><li>Autowire加载Bean可能失败的原因<ul><li>被加载的Bean没有加上@Service相关的注解</li><li>加了@Service的注解,但没有被@ComponentScan扫描。<div class="note info"><p>在Springboot中, @<mark class="label danger">SpringBootApplication</mark> 注解包含了@<mark class="label danger">ComponentScan， 这就是为什么在Spring Boot应用中看不到ComponentScan注解的原因</mark>              </p></div></li><li>Spring MVC 的filter不能加载某个Bean, 因为在这个时候Bean还没有被初始化. 解决的办法是拿到<mark class="label danger">ApplycationContext</mark> 然后调用<mark class="label danger">getBean</mark>方法.</li><li>循环依赖</li></ul></li><li>Autowired可以注入到构造器，字段，属性，参数</li></ul></li><li>@Resouce - Spring特有的注解，默认按名字查找，找不到，按类型查找，如果按类型找到多个，再按照@Qualifier筛选</li><li>@Inject - 非Spring特有的注解,</li></ul></li><li>有哪些注解可以让Bean被加载?<ul><li>@Component - 作用在类上, 用来描述通用的Bean, 业务内涵不是特别强.</li><li>@Controller - 作用在类上，与Spring mvc强相关，不能用别的注解替换， 内部会包含路由等信息</li><li>@Service - 作用在类上, 一般情况下从Repository拿数据然后提供数据给Controller消费，</li><li>@Repository - 作用在类上， 语义上来说与持久化相关，对应DAO, 不能用别的注解替换, 内部会包含SQL语句的信息.</li><li>@Bean - Spring 3.0引入，和@Configuration一起工作. 如果存在第三方插件，很显然无法使用@Component注解的，所以可以在某个方法A上使用Bean注解，然后在方法A的内部返回实例.</li></ul></li></ul></li></ul><h4 id="一些额外发现的问题"><a href="#一些额外发现的问题" class="headerlink" title="一些额外发现的问题"></a>一些额外发现的问题</h4><ul><li>ApplycationContext的getBean和@Autowired的区别是什么？</li></ul><h4 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h4><ul><li>依赖查找是什么？</li><li>规范<ul><li><a href="https://jcp.org/en/jsr/detail?id=330" title="" target="">JSR-330</a><ul><li>Spring 从3.0开始支持JSR-330规范</li><li>@Inject和Spring的@Autowired等价</li><li>@Named和Spring的@Component等价</li></ul></li><li><a href="https://jcp.org/en/jsr/detail?id=250" title="" target="">JSR-250</a></li></ul></li></ul><h3 id="开发者的角度-1"><a href="#开发者的角度-1" class="headerlink" title="开发者的角度"></a>开发者的角度</h3><h3 id="DI-时序图"><a href="#DI-时序图" class="headerlink" title="DI 时序图"></a>DI 时序图</h3><p> <img src="/images/spring/di.jpg" alt="avatar"></p><h2 id="未来"><a href="#未来" class="headerlink" title="未来"></a>未来</h2>]]>
    </content>
    <id>https://blog.yangricky.com/2021/06/20/spring-ioc/</id>
    <link href="https://blog.yangricky.com/2021/06/20/spring-ioc/"/>
    <published>2021-06-20T15:53:16.000Z</published>
    <summary>
      <![CDATA[<p>对于一个系统的使用者来说，源码就是一个黑盒，不需要关心太多. 正如陀螺仪之于手表，集成电路之于CPU一样，仅仅需要了解这个系统可以做什么，以及这个系统不可以做什么就可以了.</p>
<ul>
<li>背景</li>
<li>IOC</li>
<li>DI</li>
</ul>]]>
    </summary>
    <title>Spring源码系列 - IOC和DI</title>
    <updated>2024-02-26T15:09:46.519Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="安全" scheme="https://blog.yangricky.com/categories/%E5%AE%89%E5%85%A8/"/>
    <category term="安全" scheme="https://blog.yangricky.com/tags/%E5%AE%89%E5%85%A8/"/>
    <category term="系统设计" scheme="https://blog.yangricky.com/tags/%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/"/>
    <content>
      <![CDATA[<p>在现实生活中，身份识别无处不在。 去银行取钱，得带上银行卡。 去公司上班，得带上门禁卡。想访问一个网站，需要先注册，才能访问其内容。<br>任何一个系统，无论是软件系统，还是真实的现实世界的组织，都需要对人进行识别，然后才能决定这个人能干什么。</p><ul><li>背景</li><li>什么是登录和授权</li><li>如何进行登录和授权</li><li>登录和授权相关的实践</li><li>登录和授权的未来</li></ul><span id="more"></span><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>登录和授权是一个软件系统里最常见的行为。<br>还记得在第一家公司使用过一个ERP系统，登录进去只能看到自己所属的模块.<br>如今应用多了，一个企业内部，可能有专门的财务软件，也有可能有外部购买的软件，基于时间就是金钱，效率就是生命的原则，在一家公司内部，如何统一管理用户的账号，在一个地方登录了系统A, 在另外一个地方可以直接登录系统B，成为一个企业必须解决的问题（SSO）.<br>我要登录极客时间，有好几种方式，但最省时的方式肯定的是第三方登录，点击微信图标， 唰唰几步就完成了登录，而不是重新注册一个账号，输入电话，电子邮件。这也体现了用户至上的设计理念。 换句话说，没有人喜欢复杂和繁琐的东西。简单，简单，还是简单.</p><h2 id="什么是登录和授权"><a href="#什么是登录和授权" class="headerlink" title="什么是登录和授权"></a>什么是登录和授权</h2><p>登录和授权是两件事，要分开说.</p><h3 id="什么是登录"><a href="#什么是登录" class="headerlink" title="什么是登录"></a>什么是登录</h3><p>登录是指一个主体可以进入系统S的行为. 这里的主体可以是人或者是一个物体，系统S可以是软件，也可以是指一个组织<br>以下是一些登录的例子</p><ul><li>打开一个web，输入用户名和密码 - 这个是最常见的行为，也是所有验证模型的雏形.</li><li>张三刷门禁进公司</li><li>插入银行卡，输入密码取钱</li></ul><p>登录的形式</p><ul><li>输入用户和密码</li><li>输入用户和密码，再加上n重验证(比如验证码，或者回答一个私密问题)</li><li>按指纹</li><li>面部识别<br>从这里看出来，登录这个行为隐藏了一个主题：更简单的方式追求更高的安全</li></ul><h3 id="什么是授权"><a href="#什么是授权" class="headerlink" title="什么是授权"></a>什么是授权</h3><p>授权是指一个主体进入系统S之后，它在系统S里面能做什么. 这里的授权是广义上的授权，和之后讨论的oAuth的概念稍微有点不一样<br>授权的例子如下</p><ul><li>登录一个电商网站之后，可以下单买东西</li><li>进入公司内部，可以做在自己的座位上，不能坐在别人的座位上。</li><li>登录进入一个ERP系统，我是销售部门的人，只能看到销售模块，不能看到财务模块.</li></ul><p>授权这个行为表明了主体在系统S里面基于特定的约束，只能做特定的事情.</p><h2 id="如何进行登录和授权"><a href="#如何进行登录和授权" class="headerlink" title="如何进行登录和授权"></a>如何进行登录和授权</h2><h3 id="单系统的登录和授权"><a href="#单系统的登录和授权" class="headerlink" title="单系统的登录和授权"></a>单系统的登录和授权</h3><p>  单系统登录设计原则</p><ul><li>密码需要设计复杂度</li><li>存在数据里的密码需要加密</li><li>最好加一些额外的验证 - 比如验证码</li><li>能以最小的成本平滑的过渡到SSO</li></ul><p>  单系统授权设计原则</p><ul><li>权限表能很好的添加或者删除权限</li><li>有专门的工具来维护权限的添加和删除</li></ul><h3 id="多系统登录和授权相关规范"><a href="#多系统登录和授权相关规范" class="headerlink" title="多系统登录和授权相关规范"></a>多系统登录和授权相关规范</h3><p>  下面是一些有关登录和授权的规范，我之所以称它们为规范是为了便于方便讨论。 因为地方会称这些规范是个协议，或者是个框架，或者从字面上看是个语言，这样概念太多，会让人迷糊不清。<br>  规范本质上是一种约束。 它描述了</p><ul><li>一个系统可以做什么</li><li>一个系统不可以做什么</li><li>一个系统如何做某件事</li></ul><h4 id="SAML"><a href="#SAML" class="headerlink" title="SAML"></a>SAML</h4><p>  SAML（Security Assertion MarkUp Language）顾名思义是一个与安全和断言有关的规范。 下面引用了维基百科的定义</p><blockquote><p>SAML is an open standard for exchanging authentication and authorization data between parties, in particular, between an identity provider and a service provider. SAML is an XML-based markup language for security assertions (statements that service providers use to make access-control decisions).</p></blockquote><p>  维基百科回答的大体还是准确，但一会说这是个标准，一会又说这是一个语言，所以很乱。<br>  从这里也可以看出, SAML主要解决这样一个问题: 如何在IDP和SP之间交换验证和授权的信息？<br>  SAML有三个角色</p><ul><li>Client - 张三</li><li>Identity Provider - 微信</li><li>Service Provider - 极客时间</li></ul><p>  <img src="/images/2020/saml-workflow.png"><br>  基于上面三个角色， SAML大体的workflow是</p><ul><li>Client 访问 Service</li><li>Service 将 Client 导向到 IDP<ul><li>Client 登录成功</li></ul></li><li>IPD将Client导向到 Service<ul><li>Service验证用户登录成功</li></ul></li><li>Client在浏览器上可以正常访问资源</li></ul><h4 id="OAuth"><a href="#OAuth" class="headerlink" title="OAuth"></a>OAuth</h4><p>  OAuth是一个关于授权的标准，它不做验证。</p><blockquote><p>OAuth 2.0 is the industry-standard protocol for authorization. OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and living room devices</p></blockquote><p>  OAuth的的角色</p><ul><li>Resource Server - 资源，比如房子</li><li>Resource Owner - 资源的拥有者，比如这个房子是属于张三的</li><li>Authorization Server - 有个组织负责给张三配房子的临时钥匙</li><li>Client - 李四</li></ul><p>  <img src="/images/2020/oauth-workflow.png"></p><p>  OAuth的故事大概是这样的：<br>  张三有一家公司，李四想去张三的公司仓库里取货，但张三不能把真正的钥匙给李四，只能委托一个机构给李四一把临时的钥匙，这把临时的钥匙只能去仓库，不能去财务室. 这把临时钥匙是不记名的，换句话说，如果王五抢劫到这把临时钥匙，也是可以去仓库取东西的.</p><p>  更技术一点就是说</p><ul><li>授权服务器如何采取一种简单又安全的方式给Client生成access token?</li></ul><p>  OAuth的花式生成access token的方式</p><ul><li>授权码</li><li>简化模式</li><li>密码模式</li><li>客户端模式</li></ul><h4 id="OpenId-Connect-OIDC"><a href="#OpenId-Connect-OIDC" class="headerlink" title="OpenId Connect(OIDC)"></a>OpenId Connect(OIDC)</h4><p>  OpenId Connect是一个专注于身份验证的规范.</p><blockquote><p>OpenID Connect is an interoperable authentication protocol based on the OAuth 2.0 family of specifications. It uses straightforward REST&#x2F;JSON message flows with a design goal of “making simple things simple and complicated things possible”. It’s uniquely easy for developers to integrate, compared to any preceding Identity protocol.</p></blockquote><p>  从上面的描述可以知道 OIDC也包含授权功能，因为它是基于OAuth2.0, 但我们要记住OIDC的主业是认证，认证是它的价值主张, 授权只是它的副业.<br>  如果说授权最终的产出是access token, 那么验证最终的产出就是id token. 基本上，只要拿到了这两种token, 就可以在各个软件系统中为所欲为的横着走了.</p><p>  OIDC的角色</p><ul><li>End User - 张三</li><li>Relying Party - 相当于OAuth的Resource Server。 比如极客时间</li><li>OpenID Provider - 身份认证服务，类似于SAML中的IDP。 比如微信</li><li>ID-Token - 包含身份认证信息的JWT</li><li>UserInfo Endpoint - 获取用户的昵称和头像等信息</li></ul><p>  OIDC的大体的WorkFlow是这样的</p><ul><li>RP 发送认证请求给 OP</li><li>OP 让用户来验证 <ul><li>用户提供正确的身份验证</li></ul></li><li>OP 生成 id token 和 access token给RP</li><li>RP 根据 access token 发送一个获取用户信息的请求.</li><li>RP 获得用户的信息</li></ul><p>  <img src="/images/2020/OIDC-workflow.jpg"></p><h3 id="相关框架"><a href="#相关框架" class="headerlink" title="相关框架"></a>相关框架</h3><h4 id="Spring-Security"><a href="#Spring-Security" class="headerlink" title="Spring Security"></a>Spring Security</h4>  <!--TODO SpringSecurity的设计初衷和目的以及核心功能和概念 --><h4 id="Spring-Shiro"><a href="#Spring-Shiro" class="headerlink" title="Spring Shiro"></a>Spring Shiro</h4>  <!--TODO SpringShiro的设计初衷和目的以及核心功能和概念 --><h4 id="KeyCloak"><a href="#KeyCloak" class="headerlink" title="KeyCloak"></a>KeyCloak</h4>  <!--TODO KeyCloak的设计初衷和目的以及核心功能和概念 -->  <!--TODO 这三个框架共同表达了什么主题？ -->  <!--TODO 这三个框架有哪些优秀的设计原则和思路？ --><h2 id="登录和授权的相关实践"><a href="#登录和授权的相关实践" class="headerlink" title="登录和授权的相关实践"></a>登录和授权的相关实践</h2><h3 id="如何设计一个权限系统"><a href="#如何设计一个权限系统" class="headerlink" title="如何设计一个权限系统"></a>如何设计一个权限系统</h3><p>  这里的权限系统是指一般意义上的权限：主体对资源能执行什么样的操作？ 比如张三可以文件执行写操作.<br>  所以权限设计是为了解决这样一个终极问题</p><ul><li>主体在什么样的条件下可以对资源执行什么样的操作， 而且随着用户的增加，添加权限的操作尽可能的简单和更安全。</li></ul><p>  一些权限模型</p><ul><li>ACL - 基本的思路是：对一个文件A需要配置张三读写权限，文件B李四需要配置写权限<ul><li>这种配置是符合直觉的，但是随着用户量的增加，需要一个一个的配，很繁琐.</li></ul></li><li>DAC (Discretionary access control) - 是基于 ACL的扩展。 它引入了组和给其他主体授权的概念<ul><li>张三可以具有对文件A读写的权限，张三属于销售组，所以销售组也可以对文件具有读的权限</li><li>文件A的拥有者张三同时也可以将文件A读的权限赋给李四，这样是自主的核心要义。</li><li>Windows和Linux权限是基于DAC的。<ul><li>Windows的权限设计不够灵活，比如说“读取和执行”是一个权限，但其实是微软将读取和执行两个权限打包在一起了，然后让用户选择允许和拒绝</li><li>Linux的的权限设计充分体现了细粒度，互斥的组合的思想。比如说权限只有三种，读，写，执行，一个文件有三大类用户: 拥有者，所属组，其他人。每大类用户有7中权限，所以总的权限组合是7<em>7</em>7</li></ul></li></ul></li><li>MAC (Mandatory access control) - 相比于DAC, MAC强调的是更安全。张三和李四同属于销售组，但是张三是经理，李四是员工，所以张三和李四对文件A是有不同对访问权限的，这个问题DAC就解决不了. MAC引入了信息敏感度这个概念, 也就是引入了一个新的维度.<ul><li>要配置资源的信息等级</li><li>要配置用户主体的信息等级</li><li>核心的财务文件只有经理级别的财务人员才能查看，但是经理级别的销售人员就不能查看.</li></ul></li><li>RBAC (Role based access control) - RBAC的核心概念是：主体，角色，资源<ul><li>一个销售角色有很多权限，比如查看合同，创建客户，删除客户等等，只要一个人是销售，它就具有这些权限</li><li>角色是权限的集合，而组是用户的集合</li></ul></li><li>ABAC (Attribute base access control) - ABAC相比于其他模型是它的表达力是最强的. 如果要表达张三上午可以对文件进行读取操作，那么上述所有模型都无能为力。ABAC可以表述为: X在Y条件对Z执行A操作, 这里的X,Y,Z,A都是可以自定义的.<ul><li>来自北京的张三在上午10点钟可以读取服务器上80端口的内容。 这段表述够复杂了吧，ABAC就可以干这事情。已经有点声明式编程的味道了.</li></ul></li></ul><h3 id="操作系统是如何存储用户信息的？"><a href="#操作系统是如何存储用户信息的？" class="headerlink" title="操作系统是如何存储用户信息的？"></a>操作系统是如何存储用户信息的？</h3><pre><code>* 协议  * X.500 - X.500是基于OSI的目录访问服务，众所周知，OSI现在已经被淘汰.  * LDAP（Lightweight Directory Access Protocol） - LDAP是一个基于X.500的目录访问的协议，但它更简单，而且支持TCP/IP，这对互联网访问非常重要.* 实现  * Active Directory - 这是LDAP在windows上的实现，它的层次结构依次是：域(Domain) -&gt; 组织单位(Orgnization Unit) -&gt; 群组(Group) -&gt; 用户(User)    * 一般来说，AD适合在内网中的C/S架构  * OpenLDAP  * ADFS - ADFS是一种跨网络的身份认证方案，也就是用户账户和应用程序位于不同的网络.    * 相比于传统的AD, ADFS可以穿透不同的网络.  * Azure AD - 是基于云上的身份认证和授权方案。    * 它支持Rest风格. 具体一点就是通过api可以拿到access token和id token, 这是开发者最关心的信息.    * 它支持多重身份验证    * 它支持多租户，租户和租户之间的数据是彻底隔离的    * 它不是主域控制器</code></pre><h2 id="登录和授权的未来"><a href="#登录和授权的未来" class="headerlink" title="登录和授权的未来"></a>登录和授权的未来</h2><p>  无论是SAML, OAuth 还是 OpenId Connect, 我希望将来只有一种认证和授权协议, 姑且命名为XAuth.<br>  它有如下特征</p><ul><li>生成和解析token是简单的</li><li>token是安全的</li><li>形成规范</li><li>.net, java以及前端有成熟框架和社区支持，且这些框架被大多数公司所使用.</li></ul><p>  所有技术无论是原则，模式，还是方式，统一是大趋势。 我们不想要两个或者多个。<br>  可以看到一些例子</p><ul><li>前些日子, .net5发布了，统一了.net core, mono.</li><li>Spring Cloud想统一解决分布式领域内的所有问题，并形成规范.</li></ul><p>  这也就是为什么我对认证和授权协议会有统一的构想的原因。</p>]]>
    </content>
    <id>https://blog.yangricky.com/2020/11/22/auth/</id>
    <link href="https://blog.yangricky.com/2020/11/22/auth/"/>
    <published>2020-11-22T14:27:09.000Z</published>
    <summary>
      <![CDATA[<p>在现实生活中，身份识别无处不在。 去银行取钱，得带上银行卡。 去公司上班，得带上门禁卡。想访问一个网站，需要先注册，才能访问其内容。<br>任何一个系统，无论是软件系统，还是真实的现实世界的组织，都需要对人进行识别，然后才能决定这个人能干什么。</p>
<ul>
<li>背景</li>
<li>什么是登录和授权</li>
<li>如何进行登录和授权</li>
<li>登录和授权相关的实践</li>
<li>登录和授权的未来</li>
</ul>]]>
    </summary>
    <title>系统行为之警卫局 - 登录和授权</title>
    <updated>2024-02-26T15:09:46.516Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="网络协议" scheme="https://blog.yangricky.com/categories/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/"/>
    <category term="安全" scheme="https://blog.yangricky.com/tags/%E5%AE%89%E5%85%A8/"/>
    <category term="网络协议" scheme="https://blog.yangricky.com/tags/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/"/>
    <content>
      <![CDATA[<p>安全，这件事情无处不在。<br>我们不希望自己的个人信息被泄漏。<br>我们不希望自己的银行卡密码被盗取。<br>我们不希望自己的一言一行被监控，我们希望能有个人的隐私.<br>作为用户获取信息的入口之一 - 浏览器，它是如何保证信息安全的？ 浏览器安全的主角Https登场了.</p><ul><li>背景</li><li>什么是Https</li><li>Https的机制是什么？</li><li>安全和Https拾遗</li></ul><span id="more"></span><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>1994年，网景公司在TCP&#x2F;IP协议栈上创建了一个传输加密层: SSL(Secure Socket Layer), 这是一个原始规范，但没有发布。<br>1995年2月，修订了规范，并发布了SSL 2.0.<br>1996年，SSL 3.0发布, 得到大规模应用.<br>IETF 觉得SSL有硬伤，只能加密HTTP，为什么不能加密所有的应用层协议呢？<br>IETF在SSL 3.0的基础上， 重新命名和设计了这个协议，取名为TLS。<br>1999年1月, TLS 1.0 发布.<br>2006年4月, TLS 1.1 发布.<br>2008年8月, TLS 1.2 发布.<br>2018年3月, TLS 1.3 发布.</p><h2 id="什么是Https"><a href="#什么是Https" class="headerlink" title="什么是Https"></a>什么是Https</h2><p>Https是一种网络协议, Https &#x3D; Http + SSL&#x2F;TLS. HTTPS是为了</p><ul><li>身份验证 - 张三给李四发消息，如何保证李四知道这个消息是张三发送的？</li><li>信息泄漏 - 张三给李四发消息，如何保证信息不泄漏给王五？</li><li>信息篡改 - 张三给李四发消息，王五拿到信息进行了篡改，李四如何知道这个信息有没有被篡改？</li></ul><p>以上三个问题可以规约成一个问题：</p><ul><li>A给B发消息，如果保证只有通信双方知道消息的内容？</li></ul><h2 id="Https的机制是什么"><a href="#Https的机制是什么" class="headerlink" title="Https的机制是什么"></a>Https的机制是什么</h2><p>在讨论Https的原理和机制之前，先确定几个大的前提</p><ul><li>操作系统是正常的 - 不要使用盗版操作系统</li><li>浏览器是正常的 - 不要下载被改造过的浏览器</li><li>CA中心是正常的 - 就好比一个市政府不能是假的.</li><li>人是有可能犯错的 - 有时候脑子短路，会有误操作.</li><li>服务端的私钥是不会被窃取的</li></ul><p>引入这些大前提的目的是</p><ul><li>方便讨论问题，否则会钻进一些牛角尖。</li><li>说明没有绝对的安全。</li></ul><h3 id="Https机制的核心理念"><a href="#Https机制的核心理念" class="headerlink" title="Https机制的核心理念"></a>Https机制的核心理念</h3><ul><li>非对称加密实现证书的传输</li><li>对称加密进行数据的传输</li></ul><h3 id="Https的两大阶段"><a href="#Https的两大阶段" class="headerlink" title="Https的两大阶段"></a>Https的两大阶段</h3><ul><li>证书验证<ul><li>客户端发起连接请求</li><li>服务端返回证书</li><li>客户端验证证书是否合法，如果不合法则给予警告和提示</li></ul></li><li>数据传输<ul><li>证书验证合法之后，客户端生产随机数，用服务端的公钥加密随机数</li><li>将随机数发送给服务端， 服务端根据随机数选择对称加密算法。</li><li>服务端对要返回的内容进行加密</li></ul></li></ul><p><img src="/images/2020/https.png"></p><h3 id="中间人攻击-Man-In-The-Middle"><a href="#中间人攻击-Man-In-The-Middle" class="headerlink" title="中间人攻击 - Man In The Middle"></a>中间人攻击 - Man In The Middle</h3><p>中间人攻击是客户端和服务端在通信的时候，通信被第三方劫持，而客户端和服务端都意识不到第三方的存在。<br>一个基本事实:</p><ul><li>证书只有服务端能拥有.</li></ul><p>在基于上面提到的几大前提的基础上，要防止中间人攻击的问题就变为：</p><ul><li>如何让客户端知道这个证书是正常的？</li></ul><p>客户端如何验证证书的有效性的？</p><ul><li>验证域名, 有效期</li><li>验证根证书来源是否合法</li><li>验证证书是否被篡改</li><li>验证证书是否被吊销<ul><li>黑名单方式 - 定期从CA下载名单列表，效率高，但不实时.</li><li>OCSP方式 - 在线验证。效率低，但实时性高.</li></ul></li></ul><p>证书的三种类型</p><ul><li>DV - 验证域名</li><li>OV - 验证域名，企业信息</li><li>EV - 验证域名，企业信息，律师函等等.</li></ul><h2 id="安全和Https拾遗"><a href="#安全和Https拾遗" class="headerlink" title="安全和Https拾遗"></a>安全和Https拾遗</h2><p>下面是近些年遇到的一些与安全相关的话题</p><ul><li>大概4年前左右的样子，公司将全部站点升级为Https.</li><li>以前数据库密码是明文保存的，现在已经改成密文了.</li><li>代码库里不同的branch有不同的权限.</li><li>一些数据库连接字符串在github仓库里是密文保存的，下载到本地之后是明文.</li><li>网站有段时间账号攻击，加了验证码作为第一道关卡。</li><li>GDPR - 对于欧洲的用户，用户同意之后，数据才能保存到Salesforce里</li></ul>]]>
    </content>
    <id>https://blog.yangricky.com/2020/11/17/https/</id>
    <link href="https://blog.yangricky.com/2020/11/17/https/"/>
    <published>2020-11-17T15:35:44.000Z</published>
    <summary>
      <![CDATA[<p>安全，这件事情无处不在。<br>我们不希望自己的个人信息被泄漏。<br>我们不希望自己的银行卡密码被盗取。<br>我们不希望自己的一言一行被监控，我们希望能有个人的隐私.<br>作为用户获取信息的入口之一 - 浏览器，它是如何保证信息安全的？ 浏览器安全的主角Https登场了.</p>
<ul>
<li>背景</li>
<li>什么是Https</li>
<li>Https的机制是什么？</li>
<li>安全和Https拾遗</li>
</ul>]]>
    </summary>
    <title>网络协议之保密局 - Https</title>
    <updated>2024-02-26T15:09:46.517Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="网络协议" scheme="https://blog.yangricky.com/categories/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/"/>
    <category term="网络协议" scheme="https://blog.yangricky.com/tags/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/"/>
    <content>
      <![CDATA[<p>凡是与电脑打交道的人，每天必然会用浏览器，只要用了浏览器就需要输入一个网址。这个网址的样子是这样的： “http:&#x2F;&#x2F;”, 没错，它就是这篇文章的主人公。它是如此的熟悉，以致于我们会忽略它的存在.<br>它的前世今生到底是什么样子？</p><ul><li>背景</li><li>http发展历史</li><li>未来</li></ul><span id="more"></span><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>1965年8月24日，Ted Nelson发表在美国计算机协会（ACM）上的论文，用到了词语<br>“hypertext”.  这里的超文本和我们现在说的超文本内涵是不一样的.<br>1989年， 当时在 CERN 工作的 Tim Berners-Lee 博士写了一份关于建立一个通过网络传输超文本系统的报告。这个系统起初被命名为 Mesh。 在随后的1990年项目实施期间被更名为万维网（World Wide Web）. 它有四个部分组成</p><ul><li>HTML - 一种用来表示超文本的格式</li><li>HTTP - 用来传输超文本的协议</li><li>网页浏览器 -  WorldWideWeb， 显示超文本的软件。可以打开<a href="https://worldwideweb.cern.ch/browser/">地址</a>，体验一下第一款网络浏览器.</li><li>服务器 - CERN httpd.</li></ul><p>从这里我们可以看到</p><ul><li>HTML是一种文档格式。再看看现在的Html5，这种变化是相当相当的巨大.</li><li>Http不干别的，就是用来显示文档的.</li><li>第一个网页浏览器</li><li>第一个web服务器</li><li>这里没有JS. 换句话说，不能和网页有交互.</li><li>这里没有CSS. 换句话说，浏览器的内容没有那么美</li></ul><p>我们应当记住这个时间点，在这个时间点上出现了好多第一。而且也应该知道，出现这些东西是简单的和朴素的。这个世界是变幻莫测的，有哪几股力量导致了http, html 变成了今天这个样子？</p><p><img src="/images/2020/http-version.png"></p><h2 id="Http发展历史"><a href="#Http发展历史" class="headerlink" title="Http发展历史"></a>Http发展历史</h2><h3 id="Http-0-9-小学生"><a href="#Http-0-9-小学生" class="headerlink" title="Http&#x2F;0.9 - 小学生"></a>Http&#x2F;0.9 - 小学生</h3><p>  Http&#x2F;0.9在1991年发布。 它是简单的。 它只支持</p><ul><li>get方法。</li><li>只能返回Html格式的文件.<br>  也没有错误码，如果发送错误，返回一个错误的Html就可以了. Http 0.9真的可以说是技能单一。</li></ul><h3 id="Http-1-0-中学生"><a href="#Http-1-0-中学生" class="headerlink" title="Http&#x2F;1.0 - 中学生"></a>Http&#x2F;1.0 - 中学生</h3><p>  Http&#x2F;1.0在1996年5月发布，它是http0.9的升级版。 它的能力相比0.9就强多了</p><ul><li>支持状态码</li><li>支持多种格式的文档返回类型</li><li>缓存 - Expires, Pragma</li><li>Keep-alive - 需要显示指定。 默认是短连接</li><li>Http头</li><li>新的Http方法 - HEAD, POST。 至此，前端开发中最常用的Get, Post方法已经全了</li></ul><p>  Http&#x2F;1.0 存在的问题</p><ul><li>连接无法复用 - 每次通信都是遵循打开连接，接受数据，关闭连接这个过程。这就导致了效率非常低下</li></ul><h3 id="Http-1-1-大学生"><a href="#Http-1-1-大学生" class="headerlink" title="Http&#x2F;1.1 - 大学生"></a>Http&#x2F;1.1 - 大学生</h3><p>  1997年处，Http&#x2F;1.1发布. Http&#x2F;1.1的特性在整个Http历史上是革命性的，如同Jquery之于前端, Spring之于Java.<br>  来看看Http&#x2F;1.1可以做什么</p><ul><li>持久连接 - 默认支持长连接</li><li>pipelining机制 - 客户端同时按顺序发送多个连接，服务端按顺序返回多个响应</li><li>新增Http方法 - OPTIONS, PUT, DELETE, TRACE, CONNECT</li><li>Host头 - 支持一个物理机部署多个站点。</li><li>新的缓存机制 - Cache-control，etag</li><li>断点续传 - content-range， 隐含了分而治之的思想。</li><li>分块传输 - transfer-encoding: chunk， 隐含了分而治之的思想。</li></ul><p>  Http&#x2F;1.1的问题</p><ul><li>线头阻塞（Head of line blocking）- HOLB</li></ul><h3 id="Http2-初入职场"><a href="#Http2-初入职场" class="headerlink" title="Http2 - 初入职场"></a>Http2 - 初入职场</h3><p>  2015年，Http2发布。 Http2与Http&#x2F;1.1的不同是, Http2在应用层面解决了线头阻塞的问题. 为了解决这个问题，等待了15年.<br>  Http2它有什么能力？</p><ul><li>多路复用<ul><li>用一个连接进行数据的收发。看到这里有些朋友是不是眼熟？Node和Redis都是用一个线程来专门做特定的事情。 创建一个连接，或者创建一个线程都是有开销的，既然如此，就用最少的连接或者线程好了。</li><li>一个场景：如果在一个连接上，同时想获取js, css, html文件应该怎么办？ 以前的做法是获取完了js文件，然后再获得css文件。一个很自然的想法是将这文件切割和剁碎。然后拿到碎片之后再进行组装，从使用的角度来看，就是并发. </li><li>流和帧<ul><li>一个流可以理解为一个请求</li><li>流可以设置优先级</li><li>流由多个帧组成。 每个帧是标记自己属于哪个流。 就好像每个人知道自己属于哪个组织.</li><li>帧是基于二进制编码的。</li><li>在传输过程中，帧和帧是乱序的。</li></ul></li></ul></li><li>基于二进制</li><li>头部压缩<ul><li>基于HPACK算法</li><li>在客户端和服务端两端维护各自的字典</li></ul></li><li>服务器推送</li></ul><p>  Http2是否真的完美无缺？ 当然不是。 Http2只是部分的解决来队头阻塞的问题. 为什么呢？<br>  因为TCP本质上是要保证顺序的，一个发送的包丢了之后，是无法收到后续发送包的响应的。 所以根据木桶原理，即使上层再怎么拆，怎么分，再怎么捣腾也没有.<br>  一场风暴正在酝酿着.</p><h3 id="Http-3-职场老油条"><a href="#Http-3-职场老油条" class="headerlink" title="Http&#x2F;3 - 职场老油条"></a>Http&#x2F;3 - 职场老油条</h3><p>  既然TCP是队头阻塞的元凶，那么把TCP干了不就可以了？这不可以，因为大多数互联网的设施是基于TCP, 简单的说就是TCP历史包袱较重. 当然了，也不可以对TCP直接修改，这些修改都牵涉到操作系统内核的更新。所以再TCP身上动念头的思路可以停止了.</p><p>  换一个思路，可不可以从UDP身上下手？让UDP发100个包，它不会多发也不会少发，但它要是包丢了，它也不管. UDP身上的行为比较简单，可以基于 UDP做些事情.<br>  于是, QUIC登场了。 QUIC的初衷是在UDP的基础上，实现和TCP类似的功能，而且要消除TCP的缺点。所以QUIC的整体战略定位还是蛮高的.<br>  QUIC协议支持：</p><ul><li>建立连接的优化</li><li>拥塞控制的优化</li><li>更好的多路复用</li><li>前向纠错特性</li><li>连接迁移<br>  这个五个特性的最终目标都是为了一个字：快.<br>  那么Http&#x2F;3是什么？ Http&#x2F;3是HTTP&#x2F;2 over QUIC.<blockquote><p>在2018年10月28日的邮件列表讨论中，互联网工程任务组（IETF） HTTP和QUIC工作组主席Mark Nottingham提出了将HTTP-over-QUIC更名为HTTP&#x2F;3的正式请求，以“明确地将其标识为HTTP语义的另一个绑定……使人们理解它与QUIC的不同”，并在最终确定并发布草案后，将QUIC工作组继承到HTTP工作组。在随后的几天讨论中，Mark Nottingham的提议得到了IETF成员的接受，他们在2018年11月给出了官方批准，认可HTTP-over-QUIC成为HTTP&#x2F;3</p></blockquote></li></ul><h2 id="未来"><a href="#未来" class="headerlink" title="未来"></a>未来</h2>]]>
    </content>
    <id>https://blog.yangricky.com/2020/11/09/http/</id>
    <link href="https://blog.yangricky.com/2020/11/09/http/"/>
    <published>2020-11-09T14:51:31.000Z</published>
    <summary>
      <![CDATA[<p>凡是与电脑打交道的人，每天必然会用浏览器，只要用了浏览器就需要输入一个网址。这个网址的样子是这样的： “http:&#x2F;&#x2F;”, 没错，它就是这篇文章的主人公。它是如此的熟悉，以致于我们会忽略它的存在.<br>它的前世今生到底是什么样子？</p>
<ul>
<li>背景</li>
<li>http发展历史</li>
<li>未来</li>
</ul>]]>
    </summary>
    <title>网络协议之万维网摇篮 - Http</title>
    <updated>2024-02-26T15:09:46.517Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="网络" scheme="https://blog.yangricky.com/categories/%E7%BD%91%E7%BB%9C/"/>
    <category term="协议" scheme="https://blog.yangricky.com/tags/%E5%8D%8F%E8%AE%AE/"/>
    <category term="网络" scheme="https://blog.yangricky.com/tags/%E7%BD%91%E7%BB%9C/"/>
    <content>
      <![CDATA[<p>网络的信道是不稳定的，有时候施工队伍一不小就把光缆给挖断了，也有时候也有可能网络上连接太多了，导致弱网环境， 这些冰山一角的事情，充分说明了网络是不可靠的. TCP设计的初衷是保证数据能在网络上进行可靠的传输。 光缆都断了，数据还怎么可靠传输呢？<br>下面，开始我们的探索之旅.</p><ul><li>什么是TCP</li><li>TCP要解决什么问题</li><li>TCP是如何解决这些问题的</li><li>拾遗</li></ul><span id="more"></span><h2 id="什么是TCP"><a href="#什么是TCP" class="headerlink" title="什么是TCP"></a>什么是TCP</h2><p>TCP全称是Transmission control Protocal, 是一个工作在传输层上的协议. 它只关心数据如何可靠和有效的到达目的地，但不关心数据如何到底目的地。</p><h2 id="TCP要解决什么问题"><a href="#TCP要解决什么问题" class="headerlink" title="TCP要解决什么问题"></a>TCP要解决什么问题</h2><p>TCP要解决的问题是数据如何可靠和有效的到达目的地。</p><ul><li>可靠<ul><li>如果发送的包丢了，应该如何处理？</li><li>数据包是有顺序的，如何保证接受方拿到的包也是有顺序的？</li></ul></li><li>有效<ul><li>如果服务端比较忙或者比较闲，客户端应该采取什么样的策略来发送包？</li></ul></li></ul><p>TCP就是为了解决这三大问题而存在。 但这三大问题会衍生一些子问题.</p><p>回到一开始的那个问题： 光缆都断了，数据还怎么可靠传输呢？<br>答案是光缆断了，数据没法可靠传输。<br>网络不可靠是一个事实和大前提， 在网络不可靠的大前提，TCP尽可能的保证可靠的数据传输。<br>一个不太恰当的比喻： 张三娶了一个漂亮老婆，这个老婆有点花心和不可靠。 但即使老婆花心和不可靠， 那日子是不是还得照样过？所以张三就想了各种办法，比如平时对老婆好点，让她收收心，平时也查查她的手机，看看是否有可疑的聊天记录。张三所做的一切就是让一个本性不靠谱的女人行为上稍微靠谱一点. </p><h2 id="TCP是如何解决这些问题的"><a href="#TCP是如何解决这些问题的" class="headerlink" title="TCP是如何解决这些问题的"></a>TCP是如何解决这些问题的</h2><p>一个完成的TCP过程是建立连接，收发数据，释放连接.</p><h3 id="前置知识"><a href="#前置知识" class="headerlink" title="前置知识"></a>前置知识</h3><ul><li>TCP的包是没有IP地址的，那么是IP层的事情.</li><li>TCP连接是点对点的，也就是说一个TCP连接对应的是两个端口.</li><li>ACKn代表着到目前为止，对方序号n-1的之前的数据都正常收到了，也代表期望对象下次发送序号为n的数据.</li></ul><h3 id="建立连接"><a href="#建立连接" class="headerlink" title="建立连接"></a>建立连接</h3><p>建立连接是为了进行可靠的数据传输做保证。就好比去拜访一位朋友，得事先打个招呼告知一下.<br>TCP建立连接是通过三次握手的策略来保证的。三次握手保证了</p><ul><li>客户端知道自己和对方有接受和发送的能力</li><li>服务端知道自己和对方有接受和发送的能力</li></ul><p>第一次握手是保证了服务端知道自己有接受能力，对方有发送能力.<br>第二次握手是保证了客户端知道自己有发送和接受能力，服务端有发送和接受能力.<br>第三次握手是保证了服务端知道客户端自己有发送的能力，客户端有接受能力.</p><p>从这里可以看出，二次握手是不能保证服务端知道自己是否有发送的能力，客户端是否有接受的能力.<br>四次握手也可以，但画蛇添足了.</p><h3 id="收发数据"><a href="#收发数据" class="headerlink" title="收发数据"></a>收发数据</h3><h4 id="客户端给服务端的发送数据包，数据包丢了怎么办？"><a href="#客户端给服务端的发送数据包，数据包丢了怎么办？" class="headerlink" title="客户端给服务端的发送数据包，数据包丢了怎么办？"></a>客户端给服务端的发送数据包，数据包丢了怎么办？</h4><p>  如果快递员给别人送货，货物弄丢了，快递员的选择很简单重发一个或者赔偿, TCP也不例外， 这就是TCP的重传策略. 对于重传，我们需要考虑以下问题 </p><ul><li>什么时候进行包重传？</li><li>重传哪些包？</li><li>如何进行重转</li></ul><p>  服务端返回 ACK 包的机制</p><ul><li>客户端发送了1, 2, 3, 4, 5 总共5个包，服务端收到了1, 2，会返回ACK3, 然后收到了4(此时没有收到 3)， 那么服务端应该返回什么？ 还是ACK3. ACKn的真正含义是服务端已经收到了n-1个连续的包, 并期望客户端发送第n个包.</li></ul><p>  假设存在一种上帝策略X, 这个策略可以让客户端及时的发送服务端所需要的包，且不重复.<br>  围绕上面三个问题，看看下面四种重传策略是如何逼近这个上帝策略的.</p><ul><li>超时重传<ul><li>超时重传的思路是：设定一个定时器，如果在规定时间内没有响应，则重发数据包.</li><li>超时重传的思路是朴素和直观的。但超时时间应该怎么定？如果时间间隔太长，则发包的效率太慢，如果时间间隔太短，会导致响应包还没收到，就重发了，就导致更多的重发。目前主流的策略是超时重传的时间（Retransmission Timeout）略大于包往返时间（Round-Trip Time）。</li><li>实际情况是 ，RTT是动态的，所以相应的 RTO也是动态的. RTO的计算规则相对复杂，具体公式可参考RFC 6298. 这里顺便说一下，这个公式的一些参数是长期实践得到的，无法从逻辑上推导出来，有点类似于现在的机器学习现状，参数调一调，发现可以得到预期的结果，但没有办法解释为什么，简单的说就是两眼一抹黑，完全靠不断的尝试.</li></ul></li><li>快速重传<ul><li>快速重传的思路是从多次发送包的响应值中找规律。</li><li>如果客户端发送1，2，3，4，5份数据， 1先到了，ack返回2，然后2因为某种原因没收到，3到了，ack返回的还是2，4，5也到了，ack还是2. 至此，客户端收到了三个ack&#x3D;2的包，就知道2出问题了。于是重发2，因为3，4，5都收到了，ack返回的是6.</li><li>上面这个例子是一个比较理想化的例子。我们需要面两个选择：究竟是重发2，还是后面的3，4，5也要重发？<ul><li>重发2 - 如果3，4，5里面没有丢包，重发2是最完美的选择。 但 3，4，5里面如果有丢包的，那么还得重发丢失的包</li><li>重发2，3，4，5 - 如果3，4，5里面没有丢包，重发3, 4, 5是一种浪费。</li></ul></li><li>快速重传仅仅解决了定时器效率的问题，但没有解决什么时候应该发2，什么时候该发3， 4，5这个问题.</li></ul></li><li>SACK - Selective Acknowledgment<ul><li>SACK的思路是服务端告诉客户端一个大体的全貌，我当前已经收到了什么，我还没收到什么。所以这也是一个比较自然的思路. 具体细节可参考 RFC 2018</li><li>SACK需要客户端和服务端同时支持</li><li>SACK会占用发送方的资源。试想一下，如果黑客劫持了服务端，给客户端发送不正常的SACK包，那么客户端就会每次都要计算服务端哪些包收到了，哪些包没有收到.</li></ul></li><li>D-SACK - Duplicate SACK<ul><li>D-SACK的核心思路是告诉客户端哪些数据是被重复接受了。具体可见RFC 2833.</li><li>通过D-SACK, 我们可以知道<ul><li>丢失的包是发送的包还是ACK包，如果丢失的包是ACK包，客户端就不要无脑重发服务端已经收到的包</li><li>先发的包后到的情况.</li></ul></li></ul></li></ul><h4 id="客户端给服务端发送数据包是按顺序发送的，服务端是如何保证拿到的一些数据包是有序的？"><a href="#客户端给服务端发送数据包是按顺序发送的，服务端是如何保证拿到的一些数据包是有序的？" class="headerlink" title="客户端给服务端发送数据包是按顺序发送的，服务端是如何保证拿到的一些数据包是有序的？"></a>客户端给服务端发送数据包是按顺序发送的，服务端是如何保证拿到的一些数据包是有序的？</h4><h4 id="客户端给服务端发送数据包的过快怎么办？"><a href="#客户端给服务端发送数据包的过快怎么办？" class="headerlink" title="客户端给服务端发送数据包的过快怎么办？"></a>客户端给服务端发送数据包的过快怎么办？</h4><p>前置知识</p><ul><li>滑动窗口 - TCP是每发一个请求，就会有一个响应，如此循环。 这明显效率太低了。有没有可能我一次发送多个请求， 然后只需要一次响应就可以了？这个思路直觉上是可行的. TCP的滑动窗口是为了协调发送方和接收方的速度. 这本质上是个生产者-消费者模型。生产者发送速度过快，消费者接受不了怎么办？增加一个中间缓存带。滑动窗口其实就是一个缓存带.<br>流量控制是TCP提供的一种机制，是为了匹配收发双方的速度.<br>以服务端接受数据为过程为例</li><li>网卡接受到的数据会放到内核缓冲区</li><li>内核缓冲区会将相应的信息挪到某一个TCP连接的接受缓存区(接受窗口就是接受缓存区)</li><li>然后应用程序会从接受缓冲区读取数据<br>客户端发送数据如下</li><li>应用程序将数据放到发送缓存区（发送窗口就是发送缓存区）</li><li>将发送窗口的数据挪到内核缓冲区</li><li>内核缓冲区的数据从网卡发送出去</li></ul><p>拾遗</p><ul><li>发送缓冲区和接受缓存区是针对一个TCP连接的。整个内核缓冲区是针对整个操作系统的</li><li>TCP头里有一个字段叫 Window代表窗口大小</li><li>窗口的大小是有接受方的窗口大小决定的.</li><li>如果客户端到的TCP window的值为0, 那么意味着服务端处在水深火热当中，没能力处理数据了。那么客户端就不会发送数据了。但是万一过会，服务端又复活了呢？ TCP是用Zero Window Probe技术，发zwp包给服务端。</li><li>窗口为0会引起死锁</li><li>糊涂窗口综合症(Silly Window Syndrome) - 如果接受方的可用窗口太小，只能容纳几个字节，发送方还在为了发几个字节需要带上很多的附加信息，显得很耗带宽资源，得不偿失。有点像服务端对客户端说我的仓库没空间了，你不要大老远过来送一些牙膏牙膏牙刷了，等我空间大了，你再送些大件过来.<ul><li>如果是服务端导致的糊涂窗口综合症，那就关闭窗口</li><li>如果是客户端导致的糊涂窗口综合症， 使用Nagels算法。核心思路是等可用的窗口变大了再发数据</li><li>Nagle算法默认是打开的，对telnet或ssh交互性比较强的程序，需要关闭这个算法。</li></ul></li><li>流量控制和用塞控制的区别是什么？<ul><li>流量控制是针对发送者和接受者之间的策略，侧重于微观，并不知道网络的整体情况。</li><li>拥塞控制是为了从宏观保证整个网络畅通的</li><li>流量控制为拥塞控制做了一小部分铺垫，但这还不够，拥塞控制还需要额外的策略.</li></ul></li></ul><h3 id="关闭连接"><a href="#关闭连接" class="headerlink" title="关闭连接"></a>关闭连接</h3><p>直观上说，建立连接是三次握手，关闭连接应该是更简单，直接一句GoodBye就完事。<br>但事实并非如此，根本原因TCP是全双工。TCP建立连接之后，双方是可以同时收发数据的，那么就意味着连接需要两个通道. 所以关闭连接就变成了如何处理两个通道.<br>两个通道都没有数据收发才是最关闭连接的标志. 关闭的真正的含义是要关闭两个通道一起关闭，但前提是两个通道都要确认没有收据收发.<br>释放连接可以用客户端和服务端任意一方发起。<br>下面是客户端是发起释放连接的过程</p><ul><li>客户端发送FIN，代表要释放发送通道</li><li>服务端收到客户端的FIN, 知道客户端没有数据要发送了. 发送ACK给客户端, 代表我同意你的请求. 但此时服务端还不能关闭连接，因为服务端可能还有数据要发送给客户端. </li><li>服务端觉得直接没有数据要发送了，就发送一个FIN请求关闭连接</li><li>客户端发送ACK给服务端，同意服务端的关闭连接请求.服务端收到ACK之后就关闭连接，客户端在等待2MSL时间之后，没有收到回复，就关闭连接.</li></ul><h3 id="整个网络发生了阻塞怎么办？"><a href="#整个网络发生了阻塞怎么办？" class="headerlink" title="整个网络发生了阻塞怎么办？"></a>整个网络发生了阻塞怎么办？</h3><p>想象一下每次过节开车回家，怎么知道回家的路是一路通畅的呢？看地图上面的交通线路状况是不是绿色，如果是红色，则代表路况拥堵. 整个网络也是出现拥堵和通畅两种情况。<br>在节假日的时候，交警应对这种情况有好几种方法 </p><ul><li>车主上高速的时候，收费站限流，只开几个闸口</li><li>车主上高速的时候，收费站完全关闭，让车主改选国道</li><li>已经在高速上的车流，交警会尽可能的疏导让它们快速的去目的地.</li></ul><p>网络拥塞发生的时候，有如下策略</p><ul><li>慢启动<ul><li>慢启动的核心原理是第一次发包的时候发一个包，第二次翻倍，以此类推。发包的数量是指数级增长。当发包的数量超过某个阀值的时候，采用拥塞避免算法。</li><li>可以看出慢启动的策略还是有点粗鲁，不够灵活。</li></ul></li><li>拥塞避免<ul><li>拥塞避免和慢启动有类似之处。当一次发送包的数量超过某个阀值的时候，那么下次发包的数量，就呈现线性增长。相比于指数增长，这个增长很慢了。</li></ul></li><li>拥塞发生<ul><li>当网络发生阻塞的时候，会发生丢包，既然发生了丢包，那么就要重传</li><li>发生超时重传的时候，会导致又要重新进入慢启动的过程</li><li>发生快速重传的时候，TCP认为你还能收到三个ACK包，说明网络还可以啊。于是将发包的数量降到一半，而不是像超时重传，将发包的数据量降到1. 然后进入快速恢复算法</li></ul></li><li>快速恢复<ul><li>快速恢复的核心是用另外一种策略来控制发生包的大小。 这个策略先不详细展开。</li></ul></li></ul><p> 至此，介绍了拥塞控制的四个算法。这四个算法的都遵循的原则是</p><ul><li>尽量的发包，而不是不发.</li><li>这个四个算法的思路都是围绕着“什么时候发多少包”展开的.这一点很像高速收费站，什么时候允许多少车辆进去.</li></ul><h2 id="拾遗"><a href="#拾遗" class="headerlink" title="拾遗"></a>拾遗</h2><h3 id="TCP粘包"><a href="#TCP粘包" class="headerlink" title="TCP粘包"></a>TCP粘包</h3><p>  客户端在发送包D的时候，服务的滑动窗口容纳不下这个包，所以只能接受一部分包D1，一个包就被拆开了。<br>  客户端在发送D1和D2两个包，服务端接受的时候，接受到一个包，包含D1和D2, 服务端无法鉴别 D1和D2.<br>  上面的这种现象是由TCP的特性决定的。 我们可以给这些现象命名。但这些是问题吗？如果是问题，TCP应该背这个锅吗？<br>  答案是：TCP不背这个锅，TCP不解决这个问题。如果这是个问题，应该由应用层去解决.</p><h3 id="TCP存在的问题"><a href="#TCP存在的问题" class="headerlink" title="TCP存在的问题"></a>TCP存在的问题</h3><p>  TCP设计的时候有个大前提：网络的包的丢失是因为网络堵塞引起的. 这个大前提直接决定了TCP的设计方向.<br>  但其实网络包的丢失可能是</p><ul><li>移动基站的弱网环境</li><li>防火墙针对性的丢包</li></ul>]]>
    </content>
    <id>https://blog.yangricky.com/2020/11/03/TCP/</id>
    <link href="https://blog.yangricky.com/2020/11/03/TCP/"/>
    <published>2020-11-03T15:31:20.000Z</published>
    <summary>
      <![CDATA[<p>网络的信道是不稳定的，有时候施工队伍一不小就把光缆给挖断了，也有时候也有可能网络上连接太多了，导致弱网环境， 这些冰山一角的事情，充分说明了网络是不可靠的. TCP设计的初衷是保证数据能在网络上进行可靠的传输。 光缆都断了，数据还怎么可靠传输呢？<br>下面，开始我们的探索之旅.</p>
<ul>
<li>什么是TCP</li>
<li>TCP要解决什么问题</li>
<li>TCP是如何解决这些问题的</li>
<li>拾遗</li>
</ul>]]>
    </summary>
    <title>网络协议之靠谱邮差 - TCP</title>
    <updated>2024-02-26T15:09:46.516Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="编程语言" scheme="https://blog.yangricky.com/categories/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
    <category term="编程语言" scheme="https://blog.yangricky.com/tags/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
    <content>
      <![CDATA[<p>众所周知, 计算机这个行业是不断发展的。 编程语言就是其中之一，要么是出现新的语言，要么是出现这个语言的特性升级。 其他领域，比如法律和医学， 知识更新迭代的速度远比不上编程领域.<br>让我来沏一杯茶，闲聊一下编程语言这个老生常谈的话题.</p><ul><li>编程语言历史</li><li>什么是编程语言？</li><li>编程语言的种类</li><li>设计一门编程语言需要解决什么问题？</li><li>编程语言的未来</li></ul><span id="more"></span><h2 id="编程语言历史"><a href="#编程语言历史" class="headerlink" title="编程语言历史"></a>编程语言历史</h2><ul><li>1842年：第一个程序</li></ul><div class="note info"><p>Ada Lovelace 在笔记本上写了一些计算机指令一样的东西，后来被公认是世界上的第一道计算机程序，只是那时计算机还没有问世。</p></div><ul><li>1957年: FORTRAN - 第一个程序员可以使用的程序</li></ul><div class="note info"><p>John Backus 发明了 FORTRAN，这是第一门真正意义上有程序员在使用的编程语言</p></div><ul><li>1959年: COBOL</li></ul><div class="note info"><p>Grace Hopper 发明了第一门企业级的编程语言，叫作“common business-oriented language”，简称 COBOL</p></div><ul><li>1964年：BASIC</li><li>1970年：Pascal</li><li>1972年: C语言 - 为Unix的出现奠定了基础</li><li>1980年: Smalltalk - 第一个支持面向对象的语言</li><li>1983年: Ada</li><li>1983年: c++</li><li>1986年: Objective-c</li><li>1987年: Perl</li><li>1991年: Python</li><li>1993年: Lua</li><li>1994年: PHP</li><li>1995年: Ruby</li><li>1995年: Java</li><li>1995年: Javascript</li><li>2001年: C#</li><li>2009年: Go</li><li>2010年: Rust</li><li>2012年: TypeScript</li><li>2014年: SWift</li></ul><p>根据公开的语言排行榜，排名前十的语言都是70年代以后诞生的语言. 这里面, C语言在底层领域是霸主，Java在应用领域独占鳌头, Python是数据处理领域的翘楚, JS是Web开发领域的不二人选.</p><h2 id="什么是编程语言？"><a href="#什么是编程语言？" class="headerlink" title="什么是编程语言？"></a>什么是编程语言？</h2><p>编程语言和中文，英文都有相似的地方，比如要符合一定的语法，但又稍许有些不同，比如人和人完全可以用汉语交流所有问题， 但计算机却没有这样一个语言能解决所有问题.<br>编程语言是给开发者使用的一种模型工具. 它符合一定的规范， 以某种方式解决了<code>特定领域的问题</code>。 这种方式背后的思想包括且不限于</p><ul><li>抽象<ul><li>抽象的目的是为了提供统一的方式来处理现实中的概念和行为</li><li>一个例子：在国家层面，不管男女老幼，都统一叫做公民，公民这个词就是一个抽象，在计算机领域可以表现另外一种名字: ID</li></ul></li><li>隔离<ul><li>软件开发的根本目的是为了控制复杂度。而分离关注点是其中的一个有效方式</li><li>一个例子： 国家以秦岭淮河为分界线，将中国的地域分为南方和北方。</li><li>隔离抽象和实现</li><li>隔离重要和非重要</li><li>隔离不变和变化</li></ul></li><li>组合<ul><li>这个世界上大多数系统是基于还原论的，软件系统也不例外。</li><li>一个系统如果是可以还原的，那么就是可以组合的.</li><li>一个生活中例子：不同的县组成了市，不同的市组成了省，不同的省组成了国家.</li></ul></li></ul><div class="note info"><p>本文为了方便讨论, 讨论的领域仅限于<code>企业级应用领域</code></p></div><h2 id="编程语言的种类"><a href="#编程语言的种类" class="headerlink" title="编程语言的种类"></a>编程语言的种类</h2><p>编程语言的种类远多于数据库和操作系统的种类，正如武林中有各种各样的门派一样，编程语言也有很多门派. 这些门派之间也是口水不断和相互鄙视的。</p><h3 id="过程式编程"><a href="#过程式编程" class="headerlink" title="过程式编程"></a>过程式编程</h3><p>  过程式编程的核心概念是结构体和过程（也可以称为函数）</p><ul><li>侧重于描述先做什么，后做什么，符合人的自然思维</li><li>缺点<ul><li>函数是全局的，没有访问控制.</li><li>项目规模变大了，代码会比较混乱，容易形成意大利面条的形式.</li></ul></li></ul><h3 id="面向对象编程"><a href="#面向对象编程" class="headerlink" title="面向对象编程"></a>面向对象编程</h3><p>  面向对象的价值主张是一切皆对象。 对象包含状态和行为. OO的鼻祖是SmallTalk，</p><blockquote><p>I thought of objects being like biological cells and&#x2F;or individual computers on a network, only able to communicate with messages (so messaging came at the very beginning – it took a while to see how to do messaging in a programming language efficiently enough to be useful)….OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP.</p></blockquote><p>  从上面这段面向对象设计的初衷我们可以看到</p><ul><li>对象类似网络的一个独立的节点</li><li>对象隐藏了内部的状态</li><li>对象和对象之间是通过消息方式来交互协作的</li></ul><p>  其中对象是一个独立的节点在不同的场景下，它可以是：组件，服务或者一个独立的系统.<br>  要实现以上特性，必须做好下面的事情</p><ul><li>抽象 - 需要不断的总结和归纳名词，将名词放到特定的层次和类别, 也正应了命名是计算机的两大难题之一</li><li>隔离 - 在良好的抽象基础上，才能进行很好的隔离. 这是一种分解思维.</li></ul><p>  基于以上特点我们可以编写任何复杂的系统. OOP是一种思想，OOP程序不一定要用OOP语言来写</p><p>  这个世界上花花草草，各色人等都可以用名词来表示，所以<code>围绕名词</code>以及名词的行为来表达这个世界是很自然的事情。因为我们上学的时候无论是学语文还是学英文都是要从区分名词，动词，形容词等开始的。 所以围绕名词来描述和表达问题对人的认知来说，是丝滑般的自然.</p><p>  一些符合面向对象思想的例子:</p><ul><li>猫是一种动物。 很多时候，我们要回答这个东西是什么</li><li>审批流程，员工A想要请假，小组长需要审批，上面的经理也需要审批，这个流程是可以画出一个协作图. 可以想象一下函数式编程如何处理这个问题</li></ul><p>  反对OO的声音</p><ul><li>对象是有状态的.</li><li>想要引用一个对象的行为，就必须把它相关的对象都引用进来</li></ul>  <div class="note info"><p>你想要香蕉，但是得到的却是拿着香蕉的大猩猩和整个丛林</p></div><p>  拾遗</p><ul><li>相比于继承，优先使用组合是业界主流。</li><li>一个大型应用项目，对对象进行正确的抽象，以及合理定义对象之间的关系意味这个大型应用已经成功了50%，在此基础上对对象的行为进行合理定义，意味着这个应用已经成功了70%</li><li>面向对象和关系型数据库是好基友</li><li>面向对象诞生在单核时代.</li><li>一个类有私有和公开的部分，私有的部分是针对维护者，公开的部分是针对使用者。在使用者眼中看来，公开的契约或者接口就是全部的功能， 使用起来能降低使用者的认知负担. 以前的老式收音机，会有音量，调频，开关三个键，这三个键就是全部的功能，满足用户的所有需求.</li></ul><h3 id="函数式编程"><a href="#函数式编程" class="headerlink" title="函数式编程"></a>函数式编程</h3><p>  函数式编程的价值主张是一切皆函数. 函数从语义上来说，强调的是行为。 它有两个核心原则</p><ul><li><code>Stateless</code> - 简而言之就是函数内部不保存任何状态.<ul><li>求解1+2+4，在过程式编程中，先算出1+2&#x3D;3，作为一个值保存在变量中，然后再加上4得到最终结果7。</li><li>求解1+2+4，在函数式编程中，函数接受输入1，2 返回结果3，然后将返回结果3和数据4作为输入传函数，最终返回结果7</li></ul></li><li><code>Immutability</code> - 输入的数据是不能被改变的. 有一个复印的函数，输入是原始照片，输出是复印件，函数执行完毕之后，原始的照片必须是完好无损的.</li></ul><p> 所有的函数式编程都必须遵守这两个核心原则. 函数式编程也是声明式编程的一种.</p><p>  函数式编程用到的技术</p><ul><li>尾递归优化</li><li>map &amp; reduce</li><li>管道 - 函数可以按顺序组合. 其思想来自Unix, 如果将这种组合思想应用到宏观领域就是服务的编排</li><li>递归 - 从直觉上定义和描述问题. 暗含了”做什么”的思想</li><li>柯里化 - 将一个函数的多个参数分解为多个函数. 暗含了分解和隔离思想</li><li>高阶函数 - 将函数A作为参数传入去，然后包装成另外一个函数再返回. 这里体现了分层和组合思想. 这是一个很重要的特性, 因为任何问题是可以分层次解决的.</li></ul><p>  一些符合函数式编程的例子:</p><ul><li>斐波拉契数列: f(x)&#x3D;f(x-1)+f(x-2)</li><li>对一组数据进行加工，排序，然后分组，然后过滤，然后转换，最终得到结果. 可以想象一下面向对象是如何处理这个问题的</li></ul><p>  函数式编程的优势:</p><ul><li>没有状态就没有伤害，很契合高并发场景.</li><li>重构代码的时候可以基于Copy的方式.</li></ul><p>  反对函数式编程的声音</p><ul><li>保证不变性是有代价的<blockquote><p>对某个下标的数组元素的修改，就需要复制整个数组.</p></blockquote></li></ul><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>函数式和面向对象都有各种的粉丝， 两波粉丝也是口水不断, 都是拿各自的优点来攻击对方的缺点.<br>正如我们前面提到的，这个世界上没有所谓的优点和缺点，只有特点.<br>脱离问题和场景谈优缺点都是耍流氓.</p><ul><li>当今的软件开发主要是在工程领域，而不是科学领域. 科学领域主要是研究理论和算法. 工程领域主要是解决现实中的业务问题. 根据二八法则，高水平的开发者只占20%,  一般水平的开发者占其中的 80% 。既然是工程问题，那么肯定有一大批开发者，这些开发者的水平也是参差不齐的。所以就需要一些相对简单的方式来满足大多数开发者写业务逻辑。而面向对象这种模型满足来80%的开发群体. 但这个理由还不是最充分的.</li><li>如果软件的规模很大，量变就会产生质变. 就需要简单的方式来写业务。只有简单符合直觉的东西，才是可读的，可理解的，然后是可维护的. 面向对象满足着一点。这是一个事实型的结论。 市面上的大型软件没有一个是基于函数式构建的.</li><li>写代码和写文章没有本质的区别. 写文章也要遵循一定的机构，比如总分总。写代码也是，但更严格. 代码是写给人看的，然后顺便可以执行.</li></ul><h2 id="设计一门编程语言需要考虑什么问题？"><a href="#设计一门编程语言需要考虑什么问题？" class="headerlink" title="设计一门编程语言需要考虑什么问题？"></a>设计一门编程语言需要考虑什么问题？</h2><p>如果要实现一门现代编程语言，需要考虑哪些方面的问题？ 从语言的能力的角度， 它需要有</p><ul><li>类型系统<ul><li>强类型系统<ul><li>如何表达基本类型？ 整型，<code>字符型</code>，布尔型.</li><li>如何表达非基本类型？ 类和函数谁是第一等公民？</li></ul></li><li>弱类型系统</li></ul></li><li>常用的算法和数据结构<ul><li>顺序存储和链式存储。 比如数组，链表</li><li>字典</li><li><code>集合</code>的各种操作。 比如sort, sum, map, filter…</li></ul></li><li>模块系统<ul><li>如何定义一个模块以及如何引入一个模块</li></ul></li><li>流程控制<ul><li>顺序，选择，循环</li></ul></li><li><strong><code>异步</code></strong><ul><li>如何优雅的处理异步</li></ul></li><li>进程和线程<ul><li>如何处理高并发问题 - 这里的搞并发是狭义上的高并发</li></ul></li><li>异常处理<ul><li>如果程序遇到非正常情况， 应该如何处理？</li></ul></li><li><strong><code>时间</code></strong><ul><li>任何语言都必须提供良好的API支持时间。包括时区转换，时间格式化等操作.</li></ul></li><li>网络<ul><li>如何收发消息？ NIO还是AIO?</li></ul></li><li>正则<ul><li>这个是大多数语言都会内置的功能。 正则表达式是一种声明式语言</li></ul></li><li>文件<ul><li>各种各样的文件操作和解析.</li></ul></li><li>数据库<ul><li>如何连接数据库</li><li>如何读写数据库</li></ul></li><li>内存的分配和回收<ul><li>如何合理的利用内存？</li></ul></li></ul><h2 id="编程语言和架构是什么关系？"><a href="#编程语言和架构是什么关系？" class="headerlink" title="编程语言和架构是什么关系？"></a>编程语言和架构是什么关系？</h2><p>编程语言和架构不是一个层次上的事物。<br>从设计重要性的角度来看， 架构的选择 &gt; 编程语言的选择.<br>编程语言不会决定架构的选择，但是会对架构的选择产生一些影响。 当选择一门编程语言的时候，选择的往往不是这门编程语言本身，而是选择的基于这门语言的框架和生态. 就好比，张三娶了李四，娶的不仅仅是李四，还包括李四的家庭.<br>在云原生时代，应用的基本单位是容器，你可以用Go来构建一个微服务，也可以用Java来构建一个微服务，当然，也可以用Nest.js来构建微服务，语言之间的口水之争会慢慢淡化。</p><h2 id="编程语言的衍生品"><a href="#编程语言的衍生品" class="headerlink" title="编程语言的衍生品"></a>编程语言的衍生品</h2><p>我们选择的往往不是一门语言，而是与此语言相关的生态。<br>先来看看以下几个基本事实</p><ul><li>Java毫无疑问是成功的.<ul><li>它有各种各样的规范</li><li>它有Spring框架,稳定运行了20年，社区很活跃</li><li>它可以运行在windows, linux, mac上</li></ul></li><li>JS是成功的<ul><li>JS 语言本身是有缺陷的, 使用起来不是那么简单.</li><li>前端只有一门JS语言，你别无选择</li><li>它有React框架，FaceBook背书，相关的社区很活跃</li><li>可以运行在各种浏览器端</li></ul></li><li>C#不是那么成功<ul><li>.net 仅仅局限于windows. 虽然.net core 和.net5 现在可以运行在Linux上，但有相当一部分时间, .net是不能运行在linux上的.</li><li>.net没有一个像样的框架，比如Spring, 所以没有形成一个相应的活跃社区</li><li>.net 与微软这家公司息息相关.</li><li>C#的语言特性无疑是优雅的</li><li>C#的使用很简单</li><li>国内大厂基本是Java系，C++系，或者Go系，没见过大厂使用.net的，典型的有京东和携程转Java</li></ul></li></ul><p>由上面的一些事实，我们可以得出一些结论</p><ul><li>一个语言的语法优雅，是一件好事， 但从技术决策的角度而言, 不是那么重要</li><li>一个语言如果在一个领域没有替代品，而且这个领域又是刚需，那么这个语言活的很好，即使这个语言本身不好用.</li><li>一个语言想要活的好，需要有一个重量级别的框架或者工具. 这个框架符合以下两个特点最好.<ul><li>这个框架是由大公司开发和维护的</li><li>这个框架是开源的，有很多开发者参与，形成了一个活跃的社区</li></ul></li></ul>  <div class="note info"><p>以上的这两个特点意味着这个框架试已经踩过各种坑，形成了自己的最佳实践和规范，小伙伴们可以放心使用，睡个好觉.</p></div><p>作为一个技术决策者来说，选择一门语言，主要考虑两个问题</p><ul><li>要解决什么问题？总不能说要研发一个编译器，选择Java语言.</li><li>这个语言背后的生态是不是强大？</li></ul>  <div class="note warning"><p>一个框架再牛，那它还是只是个工具，要做到心中无框架，才能无招胜有招.</p></div><h2 id="编程语言的未来"><a href="#编程语言的未来" class="headerlink" title="编程语言的未来"></a>编程语言的未来</h2>]]>
    </content>
    <id>https://blog.yangricky.com/2020/10/31/programming-language/</id>
    <link href="https://blog.yangricky.com/2020/10/31/programming-language/"/>
    <published>2020-10-31T15:37:11.000Z</published>
    <summary>
      <![CDATA[<p>众所周知, 计算机这个行业是不断发展的。 编程语言就是其中之一，要么是出现新的语言，要么是出现这个语言的特性升级。 其他领域，比如法律和医学， 知识更新迭代的速度远比不上编程领域.<br>让我来沏一杯茶，闲聊一下编程语言这个老生常谈的话题.</p>
<ul>
<li>编程语言历史</li>
<li>什么是编程语言？</li>
<li>编程语言的种类</li>
<li>设计一门编程语言需要解决什么问题？</li>
<li>编程语言的未来</li>
</ul>]]>
    </summary>
    <title>编程语言概述</title>
    <updated>2024-02-26T15:09:46.518Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="网络" scheme="https://blog.yangricky.com/categories/%E7%BD%91%E7%BB%9C/"/>
    <category term="协议" scheme="https://blog.yangricky.com/tags/%E5%8D%8F%E8%AE%AE/"/>
    <category term="网络" scheme="https://blog.yangricky.com/tags/%E7%BD%91%E7%BB%9C/"/>
    <content>
      <![CDATA[<p>我们这个世界为什么存在这么多语言？如果世界上只有一种语言，那么每个人都不需要学英语了，想想都很美.<br>相传在远古时候，人们想建造一座高可通天的塔，可以彰显自己的名气，这样也可以防止人们走散之后仍然可以找到回家的路。上帝一看，这可不对，于是设法改变了不同人的口音，这样大家就无法交流，通天塔的建造就此搁浅.</p><span id="more"></span><p>人和人之间说话是通过某种语言来沟通的，比如英语或者汉语。计算机和计算机之间通信也不例外，也需要语言，但这个语言和人类说话的语言稍微不一样，计算机和计算机之间会有不同的功能，不同的功能需要不同的语言，这里的语言往往称为协议.</p><ul><li>背景</li><li>什么是网络协议?</li><li>如何设计网络协议</li><li>有哪些网络协议</li></ul><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>试想一下，如今的互联网由哪些因素组成? 我们大概会想到</p><ul><li>线缆</li><li>网络设备 - 交换机，路由器，网卡</li><li>运营商 - 比如中国电信</li><li>防火墙</li><li>终端设备 - 可以是PC, 手机或者任何的智能设备</li><li>终端设备上的操作系统和应用软件</li></ul><p>上面有提到计算机和计算机通信需要协议，那么这个协议到底存在什么地方呢？ 答案是操作系统.<br>网络协议的数量有很多，这里聊聊一些常用的的协议.</p><h2 id="什么是网络协议"><a href="#什么是网络协议" class="headerlink" title="什么是网络协议"></a>什么是网络协议</h2><p>网络协议是计算机和计算机之间为来完成特定功能进行通信的某种约定.<br>现在我们知道谈到网络协议有两个概念必须要讨论： OSI和TCP&#x2F;IP</p><p>先说说OSI, OSI是ISO组织制定的一个模型，用来指导网络协议设计. 很明显这个模型是分层的，至于为什么要分层？这个问题本质上和软件要分层是一样的，之前有专门讨论“分解”这个话题. OSI模型的大概分为七层</p><ul><li>应用层 - 用户直接接触到的，比如电子邮件客户端</li><li>表示层 - 电子邮件有特定的格式，怎么转换成标准的传输格式呢？</li><li>会话层 - 发送端什么时候建立连接，什么时候释放连接？</li><li>传输层 - 这一层只关心数据包如何可靠达到目的地，比如这个包是不是完整的</li><li>网络层 - 这一层负责认路，比如有几条路，如何更快达到目的地</li><li>数据链路层 - 将数据帧转换为0&#x2F;1比特流</li><li>物理层 - 将0&#x2F;1比特流转为高低电平</li></ul><p>这个模型很难说它是基于哪一个维度进行拆分的。我们对人进行分类，可以基于性别分类：男人和女人，也可以基于地域分类：南方人和北方人， 这很符合直觉。 但我也从中隐约看出了两条线索</p><ul><li>信息是如何变成高低电平？“Hello World” -&gt; 统一的字符编码 -&gt; 二进制 -&gt; 高低电平</li><li>信息是如何又快又好的达到目的地?</li></ul><p>正如我们所知, OSI并没有称为主流协议，为什么？不实用，或者说不接地气.</p><p>后来的事情，大家都知道了, TCP&#x2F;IP成为主流.<br>TCP&#x2F;IP这个名字顾名思义是TCP协议和IP协议，但这个是错觉, TCP&#x2F;IP是一个协议大家族，是互联网协议的统称, 我们知道的Telnet, ICMP，SSH都属于TCP&#x2F;IP协议.<br>但我们更想知道为什么TCP&#x2F;IP协议胜出了？或者说TCP&#x2F;IP协议做对了哪些事情？ 有以下几个因素</p><ul><li>开放性 - TCP&#x2F;IP是由IETF讨论决定的，IETF是一个任何人都可以加入讨论的组织，这充分证明了群众的力量是强大的.</li><li>实用性 - TCP&#x2F;IP先有实现，然后才有规范。也就是规范发布出来的时候，已经有操作系统实现了这个协议。而不是像OSI整天在天上飘</li></ul><p>TCP&#x2F;IP 也是基于分层的</p><ul><li>应用层</li><li>传输层</li><li>网络层</li><li>数据链路层</li></ul><h2 id="如何设计一个网络协议"><a href="#如何设计一个网络协议" class="headerlink" title="如何设计一个网络协议"></a>如何设计一个网络协议</h2><p>网络协议不可能从天而将，所以需要人来制定，既然是一种沟通工具，那么自然这个工具要符合又快又好的标准了.</p><ul><li>快<ul><li>让网络的传输变快</li></ul></li><li>好<ul><li>安全</li><li>最好一种协议能解决多个问题，如果不能解决多个问题，那么就聚焦于一个问题</li><li>简单</li><li>可以扩展</li></ul></li></ul><h2 id="有哪些网络协议"><a href="#有哪些网络协议" class="headerlink" title="有哪些网络协议"></a>有哪些网络协议</h2><ul><li>应用层协议<ul><li><p>HTTP</p></li><li><p>DNS</p></li><li><p>Ping<br>PING是应用层直接使用网络层ICMP的一个例子，它没有通过运输层的TCP或UDP. 一个系统允许Ping取决于两个因素，任意一个被禁止就不能被Ping</p><ul><li>内核参数</li><li>防火墙<br>如果Ping被禁用，我又想知道远程主机是否还可以连接，那怎么办？ 可以通过tcping这个工具来测试某个端口是否开放.</li></ul></li><li><p>nslookup - 用来测试域名是否可以正常解析</p></li><li><p>Telnet - 用来进行远程登录</p></li><li><p>SSH - 安全版的Telnet</p></li></ul></li><li>传输层协议<ul><li>TCP - 让数据可以可靠达到目的地</li><li>UDP - 牺牲正确性换取效率的协议</li></ul></li><li>网络层协议<ul><li>ICMP</li><li>traceroute</li><li>ARP</li></ul></li><li>数据链路层协议</li></ul>]]>
    </content>
    <id>https://blog.yangricky.com/2020/10/27/network-protocol/</id>
    <link href="https://blog.yangricky.com/2020/10/27/network-protocol/"/>
    <published>2020-10-27T15:36:30.000Z</published>
    <summary>
      <![CDATA[<p>我们这个世界为什么存在这么多语言？如果世界上只有一种语言，那么每个人都不需要学英语了，想想都很美.<br>相传在远古时候，人们想建造一座高可通天的塔，可以彰显自己的名气，这样也可以防止人们走散之后仍然可以找到回家的路。上帝一看，这可不对，于是设法改变了不同人的口音，这样大家就无法交流，通天塔的建造就此搁浅.</p>]]>
    </summary>
    <title>网络协议大观园</title>
    <updated>2024-02-26T15:09:46.518Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="网络" scheme="https://blog.yangricky.com/categories/%E7%BD%91%E7%BB%9C/"/>
    <category term="网络" scheme="https://blog.yangricky.com/tags/%E7%BD%91%E7%BB%9C/"/>
    <content>
      <![CDATA[<p>在之前我有提到，人类社会发展有一条线索是关于信息的，人类社会的发展史就是一个信息数量越来越多，信息连接越来越快，信息载体越来丰富的历史.<br>想想古时候，将军打了胜仗，如何将捷报传给皇帝呢？只能通过马，每个几十公里，会有一个驿站，这已经是当时的最快方式。试想一下，如果那个时候能打个电话给皇帝，这效率该有多高啊。</p><span id="more"></span><p>现在的马拉松比赛就是为了纪念菲迪皮茨这样一位古时的英雄.</p><blockquote><p>希波战争波斯人和雅典人在离雅典不远的马拉松海边发生的，雅典人最终获得了反侵略的胜利。为了让故乡人民尽快知道胜利的喜讯，统帅米勒狄派一个叫菲迪皮茨的士兵回去报信。<br>菲迪皮茨是个有名的“飞毛腿”，为了让故乡人早知道好消息，他一个劲地快跑，当他跑到雅典时，已上气不接下气，激动地喊道“欢……乐吧，雅典人，我们……胜利了”说完，就倒在地上死了。</p></blockquote><p>要是有了现在的通信技术，一切都会变的不一样。 互联网发展至今，已经有几十年历史了，如果它渗透到人们生活的方方面面，以致于人们认为互联网的存在就像水存在一样自然.</p><p>以史为鉴，可以知兴衰. 把时钟拨回到从前，看看之前的通信和互联网历史.</p><ul><li><p>1839年：真正投入使用营运的电报线路于在英国最先出现</p></li><li><p>1850年：首条海底电缆横越英吉利海峡，把英国及欧洲大陆连接起来</p></li><li><p>1865年：麦克斯韦发表论文《电磁场的动力学理论》，预言了光的电磁波理论.</p></li><li><p>1895年：意大利人马可尼首次成功收发无线电电报</p></li><li><p>1969年：阿帕网是第一个使用包交换技术的网络</p><blockquote><p>1969年10月29日，斯坦福大学和加州大学洛杉矶分校的计算机首次连接了起来, 它们是互联网上的第一台主机. 在网络上发送的第一条消息应该是“Login”，但据报道，在发送字母“g”的时候，连接断了</p></blockquote></li><li><p>1969年：Unix</p></li><li><p>1971年：电子邮件出现.</p><blockquote><p> 电子邮件于1971年首次被Ray Tomlinson开发出来，他也是那个决定使用“@”符号将用户名和电脑名字（后来变成了域名）分开的人</p></blockquote></li><li><p>1971年：古登堡计划和电子图书</p><blockquote><p>第一次有人想让图书变成电子书</p></blockquote></li><li><p>1972年：CYCLADES</p><blockquote><p> 法国于1972年开始建立自己的类似于阿帕网的项目. 创新的想法：主机只负责数据的传输而不是网络本身</p></blockquote></li><li><p>1973年：跨大洲的连接和电子邮件的普及</p><blockquote><p>阿帕网第一次跨过了大西洋，和英国伦敦的一所大学连了起来。同一年，电子邮件占所有网络应用的75%</p></blockquote></li><li><p>1974年：TCP&#x2F;IP协议的诞生</p></li><li><p>1975年：电子邮件客户端</p><blockquote><p>电子邮件第一次有了转发和回复功能</p></blockquote></li><li><p>1977年：电脑上的调制解调器</p></li><li><p>1978年：电子公告栏系统（BBS）- 网络上的黑板</p></li><li><p>1978年：垃圾邮件的诞生</p><blockquote><p>Gary Thuerk给加利福尼亚的600个用户发了垃圾邮件</p></blockquote></li><li><p>1979年：MUD——最早的多角色游戏</p><blockquote><p>MUD是完全基于文本的虚拟世界，将角色扮演游戏、互动、剧情和网上聊天结合在了一起</p></blockquote></li><li><p>1979年：新闻组（Usenet）</p><blockquote><p>新闻组是一个基于互联网的讨论系统</p></blockquote></li><li><p>1982年：第一个表情</p><blockquote><p>1982年Scott Fahlman在一个笑话之后用了一个:-）</p></blockquote></li><li><p>1983年：阿帕网上的计算机通过TCP&#x2F;IP交换数据</p><blockquote><p> 阿帕网开始通过Vinton Cerf开发的TCP&#x2F;IP协议交换数据。数以百计的电脑都连到了交换机上，服务器这一名字也是83年开始出现的.</p></blockquote></li><li><p>1984年：域名系统（DNS）</p><blockquote><p>DNS最大的意义是让IP地址有了一个名字，这样人们会更容易记住这个名字.</p></blockquote></li><li><p>1985年：虚拟社区</p><blockquote><p>Stewart Brand和Larry Brilliant于85年2月开发。它开始是为了让全球的读者和作者进行交流，并且是一个开放的但是却是“有文化底蕴的、高智商的”人的聚会点</p></blockquote></li><li><p>1986年：协议战争</p><blockquote><p>OSI PK TCP&#x2F;IP, 最终OSI倒下.</p></blockquote></li><li><p>1987年：互联网在成长</p><blockquote><p>1987年，互联网上有近三万台主机。以前的阿帕网协议只能限于有1000台主机，但是采用了TCP&#x2F;IP标准后，使得有更多的主机变成了现实</p></blockquote></li><li><p>1988年：IRC——互联网中继聊天</p><blockquote><p>实时聊天的先驱</p></blockquote></li><li><p>1988年：第一次恶意的攻击</p><blockquote><p> 第一个主要的互联网蠕虫是1988年发行的。它被称为“莫里斯蠕虫”，作者是Robert Tappan Morris，导致了大部分地区的互联网的中断。</p></blockquote></li><li><p>1989年：美国在线（AOL）诞生了 - 最大的上网服务提供商</p></li><li><p>1989年：万维网（WWW）的推出</p></li><li><p>1990年：第一个商业性的拨号上网ISP</p></li><li><p>1990年：万维网协议尘埃落定</p></li><li><p>1991年：第一个网页诞生了</p><blockquote><p>前端开发者应该非常激动</p></blockquote></li><li><p>1991年：第一个基于内容的搜索协议 - Gopher</p></li><li><p>1991年：MP3成为标准</p></li><li><p>1991年：第一个摄像头</p><blockquote><p>它部署在剑桥大学的计算机实验室，其唯一目的是监视一个咖啡壶，使实验室用户可避免将时间浪费在一个空的咖啡壶上</p></blockquote></li><li><p>1993年：Mosaic——第一个图形化浏览器</p></li><li><p>1993年：白宫和联合国的网站上线了</p></li><li><p>1994年：网景浏览器</p></li><li><p>1994年: 中国正式接入因特网</p><blockquote><p>用得是一根64K的国际专线</p></blockquote></li><li><p>1994年: 国内第一个Web服务器</p><blockquote><p>1994年5月15日，中科院高能物理研究所，建立了国内第一个Web服务器，推出中国首套网页，用于介绍高科技发展情况，其中一个栏目还提供包括经济、文化、商贸等方面的信息，后更名为“中国之窗”</p></blockquote></li><li><p>1995年：互联网进入商业领域 </p><blockquote><p>这一年出现了两家公司，一个是Echo Bay, 另外一个是Amazon</p></blockquote></li><li><p>1995年：雅虎成立</p></li><li><p>1995年: 中国第一个互联网接入服务商</p></li><li><p>1996年: 国内第一家网吧</p></li><li><p>1996年：第一个基于网络的服务HoTMaiL出现</p></li><li><p>1997年：博客出现</p></li><li><p>1997年：Netflix</p></li><li><p>1998年：第一个不是靠传统媒体报道的新闻 - 克林顿&#x2F;莱温斯基的性丑闻</p></li><li><p>1998年：Google - 现在还活得很滋润</p></li><li><p>1998年：基于互联网的文件共享</p></li><li><p>1999年：利用闲置的资源进行分布式计算</p><blockquote><p>该项目是一个通过互联网利用世界范围内的300多万台计算机进行计算的分布式计算项目，一旦计算机处于屏幕保护状态，那么意味着计算机就处于空闲状态了，这样就可以利用这些计算机的处理能力了。该项目目的是通过分析天文数据来探索外星球智能的迹象</p></blockquote></li><li><p>2000年：网络泡沫破裂</p></li><li><p>2001年：维基百科发布</p></li><li><p>2003年：网络电话</p></li><li><p>2003年：MySpace成了最流行的社交网络</p></li><li><p>2004年：Web 2.0 - 这也是前端领域的一个转折点</p></li><li><p>2004年：社会化媒体和Digg</p><blockquote><p>Digg对传统的发现和产生网络内容的方式产生了革命性的影响，新闻和网站连接全都是由社区投票民主决定</p></blockquote></li><li><p>2004年：Facebook</p></li><li><p>2005年：YouTube</p></li><li><p>2006年：Twitter</p></li><li><p>2007年：网络电视</p><blockquote><p>Hulu在2007年首次推出，与美国广播公司、全国广播公司和Fox合资，目的是使流行的电视节目可以在网上观看</p></blockquote></li><li><p>2007年：iPhone</p></li><li><p>2008年：网络选举</p></li></ul>]]>
    </content>
    <id>https://blog.yangricky.com/2020/10/27/history-of-internet/</id>
    <link href="https://blog.yangricky.com/2020/10/27/history-of-internet/"/>
    <published>2020-10-27T05:39:06.000Z</published>
    <summary>
      <![CDATA[<p>在之前我有提到，人类社会发展有一条线索是关于信息的，人类社会的发展史就是一个信息数量越来越多，信息连接越来越快，信息载体越来丰富的历史.<br>想想古时候，将军打了胜仗，如何将捷报传给皇帝呢？只能通过马，每个几十公里，会有一个驿站，这已经是当时的最快方式。试想一下，如果那个时候能打个电话给皇帝，这效率该有多高啊。</p>]]>
    </summary>
    <title>互联网大事记</title>
    <updated>2024-02-26T15:09:46.517Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="数据库" scheme="https://blog.yangricky.com/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    <category term="分布式" scheme="https://blog.yangricky.com/tags/%E5%88%86%E5%B8%83%E5%BC%8F/"/>
    <category term="数据库" scheme="https://blog.yangricky.com/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    <content>
      <![CDATA[<p>事务这个词在开发世界里默认是和数据库绑定在一起，但其实其他领域里也有事务这个词，比如会计事务所，律师事务所，不同领域的事务所指的意思是不一样的。 这里主要聊聊</p><ul><li>什么是事务</li><li>为什么是事务</li><li>如何实现一个事务</li><li>总结</li></ul><span id="more"></span><h2 id="什么是事务"><a href="#什么是事务" class="headerlink" title="什么是事务"></a>什么是事务</h2><p>在不加任何限定的情况下，这里的事务默认是指数据库领域的的事务，如果是其他领域的事务会特别说明。</p><h3 id="数据库事务"><a href="#数据库事务" class="headerlink" title="数据库事务"></a>数据库事务</h3><p>一般来说，如果一个数据库支持以下四个特性，我们说这个数据库支持事务</p><ul><li>Atomic <ul><li>一个事务由一系列操作组成，这些操作要么全部完成，要么不做。从系统的角度来看，这个事务只有完成和未完成两种状态，不存在完成一半这个说法。 换句话说，做事不要半吊子或者说做事有始有终.</li></ul></li><li>Consistent<ul><li>在事务执行之前和事务执行之后，整个系统的状态是符合逻辑的。</li><li>一致的概念往往是指A和B的状态行为一致，就比如军训的时候，我们要求大家的步伐一致。 但是数据库的一致性可不是这个意思，它的一致性更多的是从系统的角度来说逻辑上的正确性。 比如张三给李四转账20快，那么张三的余额就少了20块，李四的余额就多20块，这个从逻辑上来说是正确的。</li><li>一致性就是指系统的正确性.</li></ul></li><li>Isolation<ul><li>事务A和事务B在执行的过程中，是不会互相干扰对方的执行结果的正确性。</li><li>事务隐含了如下事情<ul><li>事务的操作对象是存储单元（表）</li><li>事务是并发的</li><li>锁是在并发过程中保持正确性的一种方式</li><li>事务的隔离级别是效率和正确性之间的权衡</li></ul></li></ul></li><li>Duraton<ul><li>事务完成之后数据变保存在存储单元里，并不能回滚。简单的说，就是覆水难收，落子无悔.</li><li>事务如果完成了一半，是可以回滚.</li></ul></li></ul><p>ACID给人的第一感觉这四个特性是平等的，正如古人所信奉的仁，义，礼，智，行，这五者是并列的。但ACID并不符合MECE原则， 更像是为了凑ACID这个单词. ACID正确的描述：</p><ul><li>从原子性，并发，存储三个维度采取策略，完成正确性这个目标</li></ul><p>这里的ACID这四个特性并没有涉及到具体的技术概念，换句话说，这是一种思想和策略，既然是思想和策略，那么意味着可以应用到宏观和微观层面.</p><h3 id="分布式事务"><a href="#分布式事务" class="headerlink" title="分布式事务"></a>分布式事务</h3><p>将上述事务的四大特性应用到分布式领域就是分布式事务.<br>在设计分布式事务的时候，有两大默认约束条件</p><ul><li>网络故障</li><li>通信延迟</li></ul><h3 id="Spring事务"><a href="#Spring事务" class="headerlink" title="Spring事务"></a>Spring事务</h3><p>Spring事务是为了支持数据库事务而提供的统一编程接口.</p><h3 id="Redis事务"><a href="#Redis事务" class="headerlink" title="Redis事务"></a>Redis事务</h3><p>Redis事务是由一系列命令组成，它满足:</p><ul><li>原子性 - 所有命令要么全部执行，要么不执行。 但Redis的原子性是伪原子性，为什么呢？因为只要有一个命令执行报错，它并不会回滚，所以Redis遇到命令执行失败，会继续执行下去，简单的说, Redis在原子性这方面更像是一个二愣子，一顿操作猛如虎.</li><li>隔离型 - 一个事务执行完毕，另外事务才会执行。这个更像是serializable的隔离级别.</li></ul><h3 id="Kafka事务"><a href="#Kafka事务" class="headerlink" title="Kafka事务"></a>Kafka事务</h3><p>Kafka的事务机制是保证生产者和消费者的操作在逻辑上是一致的 </p><h3 id="软件事务内存"><a href="#软件事务内存" class="headerlink" title="软件事务内存"></a>软件事务内存</h3><h3 id="Http事务"><a href="#Http事务" class="headerlink" title="Http事务"></a>Http事务</h3><p>HTTP事务在这里顺便提一下，它与上面所说的事务没有什么关系.<br>一个Http事务是由一个Http响应和一个Http请求组成.<br>Http事务是一家之言，有时候会让人和联想到数据库里的事务，但只要记住Http事务和别的事务没有任何关系.</p><h2 id="为什么是事务"><a href="#为什么是事务" class="headerlink" title="为什么是事务"></a>为什么是事务</h2><p>事务本质上是一种策略和机制，是为了保证系统的正确性，所以事务的出现是必然的.</p><h2 id="如何实现一个事务"><a href="#如何实现一个事务" class="headerlink" title="如何实现一个事务"></a>如何实现一个事务</h2><h3 id="如何实现分布式事务"><a href="#如何实现分布式事务" class="headerlink" title="如何实现分布式事务?"></a>如何实现分布式事务?</h3><ul><li>策略<ul><li>两阶段提交协议<ul><li>第一阶段：事务管理器发生CanCommit消息给资源管理器，每个资源管理器在本地执行操作但不提交（有点类似于正式打仗之前实战演练一遍），然后发送Yes&#x2F;No给事务管理器， 代码本地操作成功或者失败。</li><li>第二阶段：事务处理器根据Yes&#x2F;NO有如下操作<br>Yes - 发送DoCommit给各个资源处理器，执行剩下的操作，执行成功发送 HaveCommited给资源处理器<br>No - 发送DoAbort给各个资源处理器，本地执行成功的操作根据事务日志做回滚，回滚到一开始的状态，就好像什么都没发生一样， 然后发送HaveCommited给资源处理器.</li><li>事务管理器接受到资源管理器的HaveCommited， 整个事务执行完毕。</li><li>MysqlSql的InnoDB事务就是基于两阶段提交的</li><li>两阶段提交协议有点类似准备打仗的时候，司令问部下，你们都战术都演练好了吗？ 部下回答都演练好了，那么司令的回复是开打，如果有部下的回答是还没，那么司令的答复是不打.</li><li>两阶段提交协议的缺点 </li><li>事务管理器必须等所有资源管理器等操作完毕才能继续下一步的操作，所以两阶段提交协议不太适合高并发场景</li><li>如果事务管理器挂了，那么整个系统就处于停滞状态</li><li>网络发生故障的时候，有些结点接受到DoCommit命令，就会执行操作，有些结点没有接受到就不会执行, 这样就导致数据不一直. 这个问题属于一个通用问题.</li><li>注意事项<ul><li>两阶段提交协议是数据库层面的协议，换句话说，相应的数据库必须支持XA协议，但一些数据库，比如MongoDB或者Cassandra并不支持XA协议, 同样Kafka也不支持.</li></ul></li></ul></li><li>三阶段提交协议<ul><li>三阶段提交协议是为了解决两阶段提交协议中的同步阻塞和网络故障导致的数据不一致问题</li><li>超时机制 - 在发送CanCommit之后，等待返回结果的这段时间内，有个时间限制，如果超过了这个限制，就放弃本次事务。 如果失败了再重试，而且失败了这个成本和代价是可以接受的，因为没有带来额外的数据损失. 超时时间和重试次数取决于业务本身.</li><li>将第一个Voting阶段拆分为CanCommit和PreCommit两个阶段.</li><li>不论是两阶段提交还是三阶段提交，它们<ul><li>是集中式事务管理，存在单点故障的风险</li><li>是同步调用</li><li>存在数据不一致的风险</li></ul></li></ul></li><li>TCC（Try-Confirm-Cancel）<ul><li>最早是由 Pat Helland 于 2007 年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出</li><li>TCC类似于两阶段提交协议，主要应用在业务层面，所以需要TCC框架.</li><li>由主业务方发起</li><li>不存在集中式事务管理单点故障的问题</li></ul></li><li>本地消息表<ul><li>本地消息表这个方案是由eBay提出</li><li>系统A不直接给消息中间件发送消息，而是将一个事务操作保存到消息表里。</li><li>系统A有个后台程序读取这个消息表，将读到的消息发送给消息中间件. 但也又可能发送失败，如果发送失败，接着发。所以系统A有如下特征<ul><li>消息不会丢失，但可能有重复</li><li>消息之间的顺序有保证</li></ul></li><li>系统B作为消费方，会遇到两个问题<ul><li>丢失消费 - 拿到消息之后，处理到一半机器挂了。 解决方案：使用ACK机制。 如果处理到一半，机器挂了，那么重启之后，还会继续收到同样的消息. </li><li>重复消费 - 在本地增加一个已经处理过的消息表，然后判断消息是不是已经处理过.</li></ul></li></ul></li><li>Saga事务<ul><li>Saga 事务源于 1987 年普林斯顿大学的 Hecto 和 Kenneth 发表的如何处理 long lived transaction（长活事务）论文</li><li>一个Saga事务是由一系列子事务组成</li><li>Saga事务由两种执行顺序<ul><li>T1, T2, T3, Tn </li><li>T1, T2, T3, Tn, Cn, C3, C2, C1</li></ul></li><li>Saga的两种恢复策略<ul><li>向前恢复: 如果T3执行失败，那么会一直重试T3直到成功为止.</li><li>向后恢复: 如果T3执行失败, 那么会执行C3, C2, C1做相应的补偿. 无论是补偿也好，还是rollback也巴，不纠结名词。 这里的补偿大体分为两类<ul><li>操作上真正的逆向， 比如 T3代表余额增加了10块钱，那么C3代表给余额减去10块钱.</li><li>操作上无法逆向，也就是覆水难收. 比如 T3代表火箭发射， 那么在这个时候C3是无法逆向的，T3失败的话，C3意味着召开一个新闻发布会。</li></ul></li></ul></li><li>Saga的子事务是如何组织的？<ul><li>协同式 - 每个子事务都知道整个事务的执行序列。事务和事务之间通过消息进行通信. 相当于一个团队里，每个人都有大局观, 积极主动性比较强.</li><li>编排式 - 有一个统一的编排器来统一管理子事务，和协同式是完全相反的思路，类似于一个团队里老大说了算的方式.</li></ul></li><li>Saga的缺点 <ul><li>Saga的事务不是隔离的，需要在应用层面采取某种策略和机制来保证.</li></ul></li><li>Saga的一些补充<ul><li>T和C是幂等的</li><li>无论执行 T, C还是执行C, T，这种两个操作逻辑上必须是等价的.</li></ul></li></ul></li></ul></li><li>基本定理<ul><li>CAP - CAP定理是在描述发生网络故障(Partition Tolerance)的时候, 一致性(Consistency)和可用性(Availability)不能两全. <ul><li>P - 分区容错性，白话一点，就是网络发生故障了，每个结点会形成一个隔离的区域. 网络一定会发生故障的，这是客观事实。 所以在讨论CAP的时候，我们往往是讨论当P发生时，应该怎么选C和A.</li><li>C - 在发生网络故障的时候，系统想要获得数据，必须等网络故障恢复，具体一点，用户看到是一个等待页面.</li><li>A - 在发生网络故障的时候，系统为了让用户不等待，可以给用户相对的可以接受的数据.</li></ul></li><li>BASE - Basically Available, Soft State eventually consistency, CAP是一个比较不接地气的定理, BASIC定理是CAP的一个补充。<ul><li>BA - 基本可用， 这个是主观性比较强的指标。 比如之前一个请求的响应时间是0.5秒， 现在一个请求的响应时间是2秒，那么这个两秒在业务层面是可以接受的。或者牺牲非核心业务和流量的可用性，保证核心业务和流量的可用性.</li><li>S - Soft Sate, 在不影响可用性的前提下, 允许同一时刻系统的状态存在中间状态. 比如将张三地址从A修改为B，突然发生了网络故障， 一部分机器上已经修改完，另一部分机器上没修改完，这时候不同机器查询张三的地址会得到不同的结果。 我们能不能容忍这种状态？取决于业务本身.</li><li>E - Eventually Consistency, 继续上面的例子，等网络故障恢复了，所有结点上的张三的地址都为B</li></ul></li></ul></li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>在上面说了这么多，无论是单机数据库事务，还是分布式数据库事务，还是应用层面的事务，都是为了解决一个问题：执行一系列操作，如何保证一系列操作完成之后，整个系统的状态是一致的？</p>]]>
    </content>
    <id>https://blog.yangricky.com/2020/10/25/transaction/</id>
    <link href="https://blog.yangricky.com/2020/10/25/transaction/"/>
    <published>2020-10-25T02:46:09.000Z</published>
    <summary>
      <![CDATA[<p>事务这个词在开发世界里默认是和数据库绑定在一起，但其实其他领域里也有事务这个词，比如会计事务所，律师事务所，不同领域的事务所指的意思是不一样的。 这里主要聊聊</p>
<ul>
<li>什么是事务</li>
<li>为什么是事务</li>
<li>如何实现一个事务</li>
<li>总结</li>
</ul>]]>
    </summary>
    <title>系统的正确性 - 事务及相关</title>
    <updated>2024-02-26T15:09:46.519Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="Java" scheme="https://blog.yangricky.com/categories/Java/"/>
    <category term="Java" scheme="https://blog.yangricky.com/tags/Java/"/>
    <category term="框架" scheme="https://blog.yangricky.com/tags/%E6%A1%86%E6%9E%B6/"/>
    <category term="Spring" scheme="https://blog.yangricky.com/tags/Spring/"/>
    <content>
      <![CDATA[<p>许多年之后，当讨论起Java开发的时候，可以不讨论安卓开发，可以不讨论多线程，可以不讨论流，可以不讨论JVM, 但Spring是一个绕不过去的话题.<br>Spring从诞生到现在已经有20年左右的历史，这个生命不如linux, 但也足够长了.<br>在前端界，还找不到和Spring框架相提并论的框架，Spring的框架在Java领域是没有对手的，可以说找不到竞品，前端至少是React和vuejs两家独大，而且也是最近几年才出来的产物.<br>这篇文章主要闲聊</p><ul><li>什么是Spring?</li><li>为什么是Spring?</li><li>如何实现一个Spring?</li><li>Spring实践？</li><li>Spring的发展</li><li>拾遗</li></ul><span id="more"></span><h2 id="什么是Spring"><a href="#什么是Spring" class="headerlink" title="什么是Spring?"></a>什么是Spring?</h2><p>2002年10月, Rod Johnson出版了一版名为《Expoert One-on-One J2EE设计和开发》的书，书中指出了Java EE和EJB组件框架中的主要缺陷。在这本书里，他提出了基于普通Java类和注入依赖的更简单的方案.<br>在这本书发布不久之后，有两位开发者说服了Rod Johnson基于书上的代码创建一个开源基础框架的项目. 2003年三位开发者创建了一个项目, 开发者yann给框架取了一个很有诗意的名字”Spring”.有点类似中国文化里蕴含的一元复始，万象更新的意思.<br>所以Spring是一个框架，致力于简化Java开发</p><p>Spring有很多功能，如果不断的去砍掉一些功能，让Spring只具备最核心的功能，应该是</p><ul><li>IOC</li><li>AOP<br>这是Spring其他的功能的基础和起点</li></ul><p>Spring当前最新的版本是5.0, 5.0要求的最低JDK版本是8, JavaEE规范是7. Spring的大版本更新至少要基于两个因素</p><ul><li>一个最低的JDK版本 </li><li>特定的JavaEE规范</li></ul><h2 id="为什么是Spring"><a href="#为什么是Spring" class="headerlink" title="为什么是Spring?"></a>为什么是Spring?</h2><p>这个问题很简单，在当时从框架层面简化Java开发的只有Spring框架，并没有其他框架，可谓一家独大.<br>就好比创业的时候，选择了一个赛道，然后这个赛道上只有一家企业，等这家企业壮大了，只要不作死，其他企业想进来也没有机会了.</p><h2 id="如何实现一个Spring"><a href="#如何实现一个Spring" class="headerlink" title="如何实现一个Spring?"></a>如何实现一个Spring?</h2><h3 id="整体设计目标"><a href="#整体设计目标" class="headerlink" title="整体设计目标"></a>整体设计目标</h3><p>  Spring框架所有做的事情都可以说是为了一个终极目的: 简化Java开发.</p><h3 id="策略"><a href="#策略" class="headerlink" title="策略"></a>策略</h3><ul><li>基于POJO的轻量级和最小侵入编程<ul><li>POJO是简单的，那就说明是容易理解的，而且不含有杂质.</li></ul></li><li>DI和面对接口实现松耦合编程<ul><li>隔离了接口和实现</li></ul></li><li>基于AOP和惯例的声明式编程<ul><li>AOP是为了隔离了核心功能和非核心功能</li><li>惯例是用户熟悉的事物</li></ul></li><li>基于AOP和模板减少样板式代码<ul><li>模版说明是一致的编程模型，减少重复代码.</li></ul></li></ul><h3 id="战略背后的思想"><a href="#战略背后的思想" class="headerlink" title="战略背后的思想"></a>战略背后的思想</h3><p>  基于上面的策略，可以总结出这些策略背后的思想</p><ul><li>符合用户的心智模型 - 简单，熟悉，一致<ul><li>用户喜欢简单和熟悉的东西. 因为简单的东西和熟悉的东西意味着可理解，可理解意味这可维护</li><li>一致的编程模型。 对于类似的东西，不可能用方法A处理事物A, 方法B处理事物B, 而是要用一个统一的方法处理事物A和事物B</li></ul></li><li>隔离<ul><li>隔离抽象和和具体，抽象代表着稳定，具体代表着不稳定</li><li>隔离核心和非核心，核心的东西代表着重要，非核心的东西代表着次要</li></ul></li></ul><p> 上面这两大思想可以用来指导架构设计和类库设计.</p><h3 id="模块图"><a href="#模块图" class="headerlink" title="模块图"></a>模块图</h3><p>  <img src="/images/2020/spring-framework-runtime.jpeg"></p><ul><li>Spring容器的三架马车, 三是个好数字，三生万物. Bean, Core, Context为Spring框架奠定了基石<br>* Bean<br>Bean是Spring组件核心中核心，正如一部电视剧的主角一样，挪走了主角了，这部剧就没有什么看点了. 正如面向对象编程里，对象是第一核心，剩下的是对象的关系和行为。Spring Bean也是如此, Bean是Spring起点. Bean主要回答了三个问题<ul><li>如何定义一个Bean</li><li>如何创建一个Bean</li><li>如何解析一个Bean<ul><li>Context<br> Context赋予了Bean更丰富的内涵，让Bean活跃起来,变的更有生命力</li></ul></li><li>获得Bean列表</li><li>解析资源</li><li>发布事件</li><li>国际化<ul><li>Core<br> Core组件是一系列工具的集合, 它的某些概念与Bean没有直接关系，比如Log功能。Core这个名字其实取的不是特别好，会让人有点疑惑,会让人感觉Core组件就是核心， 核心意味着所有的组件都围绕着Core转，其实并不是，Spring的核心是Bean。拿打仗做个比喻, Bean是冲锋陷阵的士兵，Context是指挥官，协调管理这些士兵, 那么Core是什么呢？更多类似于后勤和伙夫, 从这里可以看出Core并不是核心. 所以把Core理解为Spring的瑞士军刀比较合适, 因为Core能提供某些贴心的小功能.</li></ul></li></ul></li></ul><h2 id="Spring实践？"><a href="#Spring实践？" class="headerlink" title="Spring实践？"></a>Spring实践？</h2><h3 id="展示"><a href="#展示" class="headerlink" title="展示"></a>展示</h3><p>  展示一般而言与Web有关，这里的展示意义更加宽泛一点，可以指手机，手表等任何显示设备. 展示层实现策略有两大方向</p><ul><li>前后端不分离</li><li>前后端分离</li></ul><h3 id="数据"><a href="#数据" class="headerlink" title="数据"></a>数据</h3><p>  任何的信息都必须是可以存储的，或者叫做持久化。这样的信息是基本事实，可以给各种方式（比如机器学习）企业的决策提供依据。 数据存储有两大类型</p><ul><li>关系型数据</li><li>非关系型数据</li></ul><h3 id="缓存"><a href="#缓存" class="headerlink" title="缓存"></a>缓存</h3><p>  用户流量增加之后，为了获得更好的用户体验，减轻数据库的压力，需要一个能抗事的家伙. 这个就是缓存.</p><h3 id="安全"><a href="#安全" class="headerlink" title="安全"></a>安全</h3><p>  对于大多数系统，必须回答两个问题</p><ul><li>这个用户是谁？</li><li>这个用户能做什么？</li></ul><h3 id="消息"><a href="#消息" class="headerlink" title="消息"></a>消息</h3><p>  任何系统都不是孤立的，系统A想和系统B相互交流，有两种办法</p><ul><li>系统A调用系统B的接口</li><li>引入一个系统M来隔离系统A和系统B, 这样系统A和系统B就完成隔离开了，符合正交的特性。换句话以后系统A即使消亡了，也与系统B没有什么关系. 这个M往往称做消息系统或者消息中间件.</li></ul><h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><p>  系统出了故障，我们希望能快速的去解决故障，而不是两眼一抹黑。换句话说，我们能对系统有掌控。 一般来说有如下方式</p><ul><li>日志</li><li>监控</li></ul><h2 id="Spring的发展"><a href="#Spring的发展" class="headerlink" title="Spring的发展"></a>Spring的发展</h2><h3 id="Spring-Boot"><a href="#Spring-Boot" class="headerlink" title="Spring Boot"></a>Spring Boot</h3><blockquote><p>Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.</p></blockquote><p>  Spring Boot不是Spring的功能升级，而是简化了Spring的构建. 使用Spring Boot</p><ul><li>没有繁琐的XML的配置</li><li>不需要操作Jar包之间的依赖关系</li><li>内置了Jetty容器，不像之前要生成一个war包，然后拷贝到特定的jetty目录下.</li></ul><p>  两个例子</p><ul><li>我们知道汽车有一键启动的功能，在此之前我们要掏出钥匙，插入钥匙，启动。 做的事情都是启动，但很明显一键启动完全提高了效率.</li><li>做过前端开发的朋友，应该知道create-react-app这个脚手架，可以大大的提搞开发者的效率，Spring-boot扮演的就是脚手架这个角色。 所以Spring boot从整个Spring的历史来看，它很重要，但没有革命性的东西。</li></ul><h3 id="Spring-Cloud"><a href="#Spring-Cloud" class="headerlink" title="Spring Cloud"></a>Spring Cloud</h3><blockquote><p>Spring Cloud provides tools for developers to quickly build some of the common patterns in distributed systems</p></blockquote><p>  从这里可以看出, Spring Cloud的定位和明确</p><ul><li>聚焦于分布式的问题 </li><li>提供一致的编程模型. 比如在这里模型下面，服务发现框架可以从容易的从ZooKeeper切换到Consul</li></ul><p>  所以Spring Cloud的目标是非常宏大, 它不像Spring Shiro是为了解决某个特定的领域的问题, 它要是统一解决分布式领域的各种问题，具体一点就是微服务和云原生相关的问题， 可谓雄心勃勃。 来看看Spring Cloud都提供来哪些工具</p><ul><li>Spring Cloud Eureka - 解决了服务注册和发现的问题<ul><li>竞品 - Nacos, k8s的Service(可以被Istio追踪，纳入servicemesh的管理)</li></ul></li><li>Spring Cloud Ribbon - 解决了如何将流量公平得分配到每台机器的问题.<ul><li>竞品 - k8s的Service</li></ul></li><li>Spring Cloud Hystrix - 解决流量过大时， 如何将让流量不再进来的问题.<ul><li>竞品 - Sentinel, k8s的Istio</li></ul></li><li>Spring Cloud Zuul - 解决了流量是否合法，以及流量进来之后去哪里的问题.<ul><li>竞品 - Kong, k8s的Ingress</li></ul></li><li>Spring Cloud Config - 解决了服务配置信息集中管理的问题<ul><li>竞品 - 携程的Apollo, k8s的ConfigMap和Secret</li></ul></li><li>Spring Cloud Stream - 解决了不同服务之间相互通信的问题.</li><li>Spring Cloud Bus - 解决了不同服务之间如何共享信息的问题</li><li>Spring Cloud Sleuth - 解决了如何了解服务全貌这个问题<ul><li>竞品 - 推特的Zipkin， 大众点评的CAT</li></ul></li><li>Spring Cloud Feign - 简化了服务的调用方式</li><li>Spring Boot Actuator - 解决了监控程序的问题<ul><li>竞品 - ZMon, DataDog</li></ul></li></ul><h2 id="拾遗"><a href="#拾遗" class="headerlink" title="拾遗"></a>拾遗</h2><p>官网中有一幅图，是从Spring boot到Spring Cloud, Spring Cloud DataFlow, 这幅图可以反应这三者之间的时间关系，但同时有个误区，它会给人传达这样一个意思</p><blockquote><p>小学生 -&gt; 中学生 -&gt; 大学生</p></blockquote><p>就是这三者是能力渐进增强的，其实并不是， Spring Boot充其量是个脚手架的角色，和Spring Cloud不是同一个层次的事物，放在一起不好比较.</p><p>如果把Spring, Spring Boot, Spring Cloud放在一起，那它们应该是一个什么样的关系呢？</p><blockquote><p>高尔夫1.4T -&gt; 高尔夫2.0T -&gt; 途观</p></blockquote><p>Spring Boot让Spring变得更快，Spring Cloud让Spring 能力更强</p>]]>
    </content>
    <id>https://blog.yangricky.com/2020/10/20/spring/</id>
    <link href="https://blog.yangricky.com/2020/10/20/spring/"/>
    <published>2020-10-20T03:12:01.000Z</published>
    <summary>
      <![CDATA[<p>许多年之后，当讨论起Java开发的时候，可以不讨论安卓开发，可以不讨论多线程，可以不讨论流，可以不讨论JVM, 但Spring是一个绕不过去的话题.<br>Spring从诞生到现在已经有20年左右的历史，这个生命不如linux, 但也足够长了.<br>在前端界，还找不到和Spring框架相提并论的框架，Spring的框架在Java领域是没有对手的，可以说找不到竞品，前端至少是React和vuejs两家独大，而且也是最近几年才出来的产物.<br>这篇文章主要闲聊</p>
<ul>
<li>什么是Spring?</li>
<li>为什么是Spring?</li>
<li>如何实现一个Spring?</li>
<li>Spring实践？</li>
<li>Spring的发展</li>
<li>拾遗</li>
</ul>]]>
    </summary>
    <title>Java之武林盟主 - Spring</title>
    <updated>2024-02-26T15:09:46.519Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="数据结构" scheme="https://blog.yangricky.com/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    <category term="树" scheme="https://blog.yangricky.com/tags/%E6%A0%91/"/>
    <category term="数据结构" scheme="https://blog.yangricky.com/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    <content>
      <![CDATA[<p>做应用开发的朋友大概这辈子不会遇到B+树这个数据结构,它的应用场景更多是在底层。<br>所有做技术的人有时候会面临一些困惑，有些技术或者理论离应用太远，有点曲高和寡，导致了我们会对这类技术丧失兴趣. 但B+树有时候就像一个幽灵一样出现在一些技术文章中，引起你的关注.<br>这篇文章主要是浅谈一下我记忆中的B+树.</p><ul><li>背景</li><li>什么是B+树？</li><li>B+树实践</li><li>拾遗</li></ul><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>要了解B+树先得说说B树, 1972年Bayer和mccreight发明了B树，但并没有说明B是什么意思, B可能代表Balance, Bayer或者Boeing,在这里就不深究B的具体含义了. B树首先它是一棵树，然后它是一颗平衡树，所有的结点都能存储数据。<br>B+树和B树的关系，大体上看就是iPhone6 plus和 iPhone6之间的关系.<br>注意：不存在B-树，这个“-”其实是个连接符，但会让人误解为是减号，如果出现B-tree其实就是B树.</p><span id="more"></span><h2 id="什么是B-树"><a href="#什么是B-树" class="headerlink" title="什么是B+树?"></a>什么是B+树?</h2><p>在讨论B+树之前，我们先要讨论一个阶的概念，就好比有些团队是大团队，有些团队是小团队，阶定义了B+树的规模。<br>满足如下条件的树，是一颗m阶的B+树</p><ul><li>根结点的数量在 [2, m] - 大Boss的下属数量范围</li><li>正常结点的子结点数量范围是[2&#x2F;m, m] - 中层骨干的下属范围</li><li>一个结点的关键字数量和指针的数量是一致的，一个关键字代表了子结点的极值 - 老板眼里只有核心员工 </li><li>所有叶子结点都在同一层，叶子结点有指针指向兄弟结点 - 真正干活的只有底层，且底层团结一致<br><img src="/images/2020/bplus-tree.png"></li></ul><h2 id="B-树实践"><a href="#B-树实践" class="headerlink" title="B+树实践"></a>B+树实践</h2><h3 id="B-树与Mysql存储引擎InnoDB"><a href="#B-树与Mysql存储引擎InnoDB" class="headerlink" title="B+树与Mysql存储引擎InnoDB"></a>B+树与Mysql存储引擎InnoDB</h3><ul><li><p>一些基本事实</p><ul><li>数据库读取数据是以页(一般而言是4k)为单位将磁盘文件加载到内存（为了方便讨论，一个页上存储一个结点）</li><li>普通磁盘加载一个页的数据大概需要10ms(旋转，寻道等操作)</li><li>磁盘的顺序读取I&#x2F;O比随机读取I&#x2F;O要快10万个量级</li><li>内存的读取速度(纳秒级别)和磁盘的顺序读取读取大约是一个量级</li><li>查询优化的核心是减少I&#x2F;O的访问次数</li></ul></li><li><p>为什么不选择B树？<br>B树的最大特点是是数据分布在所有的结点中，所有在进行范围查找时候，加载多个页的时候，会很耗时</p></li><li><p>B+树的优势<br>B+树存储数据的特点</p><ul><li>叶子节点存放了行数据</li><li>所有的叶子结点都在同一层</li><li>所有的叶子结点可以形成一个双向链表</li><li>叶子结点所在的页分布在不同的磁盘块上</li><li>非叶子结点不存储行数据，仅仅为了存储更多的索引键</li></ul><p>通过上面的特点，我们可以知道</p><ul><li>B+树相比B树很明显的一个特点是树的高度降低了，这样I&#x2F;O访问就少了.</li><li>存储和索引被隔离开来，软件设计的一个原则就是分离关注点</li></ul></li></ul><p>   回头再看看上面那个B+树的图，它有点像什么？对，像跳表. 跳表的核心思想是让链表具有二分查找能力.<br>   所以跳表和B+树的源头思想是一致的.</p><h2 id="拾遗"><a href="#拾遗" class="headerlink" title="拾遗"></a>拾遗</h2><ul><li>由于B+树的读写会导致逻辑上相邻的数据实际在物理上相聚很远，LSM树(日志结构合并树)是对B+树的改进</li></ul>]]>
    </content>
    <id>https://blog.yangricky.com/2020/10/18/B-plus-tree/</id>
    <link href="https://blog.yangricky.com/2020/10/18/B-plus-tree/"/>
    <published>2020-10-18T08:50:25.000Z</published>
    <summary>
      <![CDATA[<p>做应用开发的朋友大概这辈子不会遇到B+树这个数据结构,它的应用场景更多是在底层。<br>所有做技术的人有时候会面临一些困惑，有些技术或者理论离应用太远，有点曲高和寡，导致了我们会对这类技术丧失兴趣. 但B+树有时候就像一个幽灵一样出现在一些技术文章中，引起你的关注.<br>这篇文章主要是浅谈一下我记忆中的B+树.</p>
<ul>
<li>背景</li>
<li>什么是B+树？</li>
<li>B+树实践</li>
<li>拾遗</li>
</ul>
<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>要了解B+树先得说说B树, 1972年Bayer和mccreight发明了B树，但并没有说明B是什么意思, B可能代表Balance, Bayer或者Boeing,在这里就不深究B的具体含义了. B树首先它是一棵树，然后它是一颗平衡树，所有的结点都能存储数据。<br>B+树和B树的关系，大体上看就是iPhone6 plus和 iPhone6之间的关系.<br>注意：不存在B-树，这个“-”其实是个连接符，但会让人误解为是减号，如果出现B-tree其实就是B树.</p>]]>
    </summary>
    <title>数据结构之B+树</title>
    <updated>2024-02-26T15:09:46.516Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="缓存" scheme="https://blog.yangricky.com/categories/%E7%BC%93%E5%AD%98/"/>
    <category term="缓存" scheme="https://blog.yangricky.com/tags/%E7%BC%93%E5%AD%98/"/>
    <category term="Redis" scheme="https://blog.yangricky.com/tags/Redis/"/>
    <content>
      <![CDATA[<p>计算机有两大问题，一个是命名问题，一个是缓存失效。缓存在整个计算机体系里无处不在</p><ul><li>CPU - CPU缓存</li><li>操作系统 - Page Cache</li><li>数据库 - 数据库缓存</li><li>浏览器 - 浏览器缓存</li></ul><p>这篇文章主要是探讨一下缓存的通用问题以及Redis相关问题</p><ul><li>缓存概述</li><li>Redis历史</li><li>什么是Redis</li><li>为什么是Redis</li><li>Redis是如何实现的</li><li>Redis实践</li><li>Redis的未来</li></ul><span id="more"></span><h2 id="缓存概述"><a href="#缓存概述" class="headerlink" title="缓存概述"></a>缓存概述</h2><h3 id="什么是缓存"><a href="#什么是缓存" class="headerlink" title="什么是缓存"></a>什么是缓存</h3><p>  没有看到缓存的通用定义，这里尝试定义一下. 我们有系统X, C, D，这三个系统有如下特征</p><ul><li>系统X可以从C和D拿到同样的数据</li><li>系统X从C拿到的数据比从D拿到的数据更快</li><li>系统C的数据是来自于D</li></ul><p>  如果上述三个条件满足，那么系统C可以称做为D的缓存.</p><h3 id="为什么需要缓存"><a href="#为什么需要缓存" class="headerlink" title="为什么需要缓存"></a>为什么需要缓存</h3><p>  如果系统D满足不了单位时间内多次请求，那么系统C一定是存在的. 原因是</p><ul><li>系统C比系统D更快，那么可以提供更好的用户体验.</li></ul><h3 id="实现一个缓存会遇到什么问题？"><a href="#实现一个缓存会遇到什么问题？" class="headerlink" title="实现一个缓存会遇到什么问题？"></a>实现一个缓存会遇到什么问题？</h3><pre><code>* 缓存更新策略  * Cache Aside Pattern  * Write / Read Through Pattern  * Write Behind Caching Pattern</code></pre><h2 id="Redis历史"><a href="#Redis历史" class="headerlink" title="Redis历史"></a>Redis历史</h2><p>2008年，一家意大利公司Merzia推出了一个几个基于Mysql的实时统计系统，但是该公司的创始人Salvatore Sanfilippo对这套系统感到失望，于是自己对这套系统量身定做了一个数据库，且在2009年开发完成, 这就是Redis. 所以这就是比较典型的思路：有了一个痛点，然后才有了一个技术。 而现在不少公司的思路是反的：不管有没有痛点，先用高大上的技术再说.<br>2010年VMware公司赞助Redis, Salvatore Sanfilippo和另外一位开发者加入了VMware公司，全职开发Redis. 所以也是当时的一个痛点问题，改变了作者的人生之路.</p><h2 id="什么是Redis"><a href="#什么是Redis" class="headerlink" title="什么是Redis"></a>什么是Redis</h2><p>我看看官网上Redis的定义:</p><blockquote><p>Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker</p></blockquote><p>在我们的印象中，数据库是指MySql, Sql Server，或者Oracle. 所以从这里看得出Redis的定位是多种多样的，可以做缓存，数据库或者消息队列.<br>但有点事实可以确定</p><ul><li>相比于Kafka, Redis的消息队列没那么专业</li></ul><p>从核心行为的角度来看：Redis是一个支持Key, value的存储系统. 换句话说, Redis的灵魂在于Key,Value.</p><h2 id="为什么是Redis"><a href="#为什么是Redis" class="headerlink" title="为什么是Redis"></a>为什么是Redis</h2><h2 id="Redis是如何实现的"><a href="#Redis是如何实现的" class="headerlink" title="Redis是如何实现的"></a>Redis是如何实现的</h2><h3 id="整体设计目标"><a href="#整体设计目标" class="headerlink" title="整体设计目标"></a>整体设计目标</h3><ul><li>如何又快又好的实现一个Key, Value存储？</li></ul><p>  所以可以设想一个最简单的Key, Value的存储， 它应该支持</p><ul><li>支持数据的增删改查</li><li>数据类型多样</li><li>最好支持集群功能 - 或者说任何的框架都应该支持集群功能</li></ul><h3 id="原则"><a href="#原则" class="headerlink" title="原则"></a>原则</h3><pre><code>N/A</code></pre><h3 id="模式"><a href="#模式" class="headerlink" title="模式"></a>模式</h3><p>  Redis也是基于Reactor模式来实现的，这一点和Netty类似.</p><h3 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h3><ul><li>高性能视角 - 对应快这个指标</li><li>高可用视角 - 对应好这个指标<ul><li>分布式维度<ul><li>主从库模式<ul><li>全量复制</li><li>基于长连接的命令传播</li><li>增量复制</li></ul></li><li>哨兵机制</li><li>集群</li></ul></li><li>容错维度<ul><li>AOF日志 - 先更新缓存，然后再更新日志，这样可以得出一个结论：只有日志里有记录，就一定有执行过缓存的命令 。<ul><li>优点</li><li>缺点</li></ul></li><li>内存快照 -  RDB 文件</li></ul></li></ul></li><li>可扩展视角 - 对应好这个指标<ul><li>数据分片</li><li>负载均衡</li></ul></li><li>Redis API</li></ul><h3 id="Redis用到的数据结构"><a href="#Redis用到的数据结构" class="headerlink" title="Redis用到的数据结构"></a>Redis用到的数据结构</h3><ul><li>跳表</li><li>布隆过滤器</li><li>位图</li><li>延时队列</li></ul><h3 id="Redis用到的算法"><a href="#Redis用到的算法" class="headerlink" title="Redis用到的算法"></a>Redis用到的算法</h3><ul><li>限流算法</li><li>GeoHash</li></ul><h2 id="Redis的实践"><a href="#Redis的实践" class="headerlink" title="Redis的实践"></a>Redis的实践</h2><ul><li>Redis做消息队列，业界有日活百万的案例.</li><li>Redis所在的宿主机挂了之后怎么办？</li></ul><h2 id="Redis的未来"><a href="#Redis的未来" class="headerlink" title="Redis的未来"></a>Redis的未来</h2>]]>
    </content>
    <id>https://blog.yangricky.com/2020/10/15/cache/</id>
    <link href="https://blog.yangricky.com/2020/10/15/cache/"/>
    <published>2020-10-15T03:09:13.000Z</published>
    <summary>
      <![CDATA[<p>计算机有两大问题，一个是命名问题，一个是缓存失效。缓存在整个计算机体系里无处不在</p>
<ul>
<li>CPU - CPU缓存</li>
<li>操作系统 - Page Cache</li>
<li>数据库 - 数据库缓存</li>
<li>浏览器 - 浏览器缓存</li>
</ul>
<p>这篇文章主要是探讨一下缓存的通用问题以及Redis相关问题</p>
<ul>
<li>缓存概述</li>
<li>Redis历史</li>
<li>什么是Redis</li>
<li>为什么是Redis</li>
<li>Redis是如何实现的</li>
<li>Redis实践</li>
<li>Redis的未来</li>
</ul>]]>
    </summary>
    <title>Java之效率管家 - Redis</title>
    <updated>2024-02-26T15:09:46.516Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="Java" scheme="https://blog.yangricky.com/categories/Java/"/>
    <category term="高性能" scheme="https://blog.yangricky.com/tags/%E9%AB%98%E6%80%A7%E8%83%BD/"/>
    <category term="框架" scheme="https://blog.yangricky.com/tags/%E6%A1%86%E6%9E%B6/"/>
    <category term="Netty" scheme="https://blog.yangricky.com/tags/Netty/"/>
    <content>
      <![CDATA[<p>万物互联是现在乃至未来的趋势. 只要互联就需要网络，不管是无线还是有线。只要牵涉到联网，就牵涉到两台机器上的应用程序之间的通信。 只要是通信，我们就需要又快又好的进行通信。那么应该有一个事物来做这样基础的事情，这样开发者可以专注于写业务逻辑.<br>这个事物就是Netty. 当然Netty不是进行Java进行网络通信的唯一选择，我们自己也可以写一个网络编程框架,但在此刻，它是网络通信框架领域那颗最耀眼的明星,这就是影响力，必须承认它的人气。当我们讨论Java网络编程框架的时候，Netty是一个绕不过去的话题. 让我来沏一杯茶，闲言碎语几句.</p><ul><li>背景</li><li>什么是Netty?</li><li>为什么是Netty?</li><li>Netty是如何实现的？</li><li>Netty的实践</li><li>网络编程框架的将来</li></ul><span id="more"></span><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>Netty是2004年由韩国人trustin lee开发出来的，同时他也是mina的作者. Netty到现在为止已经由16年的历史，我有理由相信它会继续存活16年</p><h2 id="什么是Netty"><a href="#什么是Netty" class="headerlink" title="什么是Netty?"></a>什么是Netty?</h2><p>Netty是</p><ul><li>网络编程框架 - Netty解决问题的领域是网络，非网络的它不关心.</li><li>异步和基于事件驱动的</li><li>容易使用，性能好 - 开发者视角</li><li>开发服务器和客户端 - 意味着Netty的使用场景不是单机</li></ul><p><img src="/images/2020/pigeon.png"></p><h2 id="为什么是Netty"><a href="#为什么是Netty" class="headerlink" title="为什么是Netty?"></a>为什么是Netty?</h2><p>截止当前这个时间点，github上还有406个问题是open的, 关闭了4810问题, 如果除以16，那么就是1年300个问题，一天一个问题。这也能说明一个问题，网络编程领域的炸弹和坑有很多，不要掉以轻心.</p><h3 id="为什么不是JDK-NIO"><a href="#为什么不是JDK-NIO" class="headerlink" title="为什么不是JDK NIO?"></a>为什么不是JDK NIO?</h3><p>Netty的技能包比JDK NIO多</p><ul><li>支持多种传输层协议</li><li>解决了粘包，半包现象</li><li>流量控制，黑白名单</li><li>处理各种个样的异常，比如断连，异常<ul><li>这让我想起了Jquery, Jquery屏蔽了各种浏览器的差异，提供了一个统一的接口给用户操作DOM.</li></ul></li></ul><p>JDK NIO 本身有很多bug, 约为5000个左右.<br>这里说明了两个问题：</p><ul><li>开发者直接使用JDK NIO是不明智的选择</li><li>开发者开发一个网络编程框架也是一个不明智的选择</li></ul><h2 id="Netty是如何实现的？"><a href="#Netty是如何实现的？" class="headerlink" title="Netty是如何实现的？"></a>Netty是如何实现的？</h2><ul><li><p>Netty设计哲学</p><ul><li>API让人用起来舒服, 这个是针对使用者的 - 这种舒服应该符合用户的感性和直觉，就好像做西红柿炒蛋的时候，放好鸡蛋之后接着放西红柿。</li><li>实现让人舒服 - 这个是针对维护者的.<blockquote><p>The answer is the philosophy it is built on. Netty is designed to give you the most comfortable experience both in terms of the API and the implementation from the day one. It is not something tangible but you will realize that this philosophy will make your life much easier as you read this guide and play with Netty.</p></blockquote></li></ul></li><li><p>Netty是基于Reactor模式实现的</p><ul><li>单Reactor单线程</li><li>单Reactor多线程</li><li>主从Reactor多线程</li></ul></li><li><p>Netty用到的设计模式</p><ul><li>单例 - ReadTimeOutException#INSTANCE</li><li>工厂 - ReflectiveChannelFatory</li><li>策略 - EventExcutorChooser</li><li>装饰 - WrappedByteBuf</li><li>模板 - AbstracTrafficShapingHandler</li><li>职责链 - ChannelPipeline</li><li>构造者 - WebSocketServerProtocolConfig.Builder</li><li>观察者 - ChannelFuture#AddListener</li></ul></li><li><p>从框架设计角度，Netty的核心概念是</p><ul><li>Channel - 可以理解为任意两点之间的连接. </li><li>EventLoop - Channel和EventLoop是多对一的关系，EventLoop和线程是一对一的关系，也就是一个Chanenel接连对应一个稳定的线程, 这样的好处是事件处理是单线程，避免并发问题. Channel通过事件的方式给EventLoop发送消息.</li><li>EventLoopGroup - 对EventLoop从功能的角度进行分类<ul><li>bossGroup - 专门用来处理连接<ul><li>bossGroup连接处理完成之后，要将连接交给workGroup，这到底选择哪个EventLoop呢？Netty使用的是轮询算法.</li></ul></li><li>workerGroup - 专门用来处理读和写</li></ul></li><li>ServerBootStrap</li><li>ChannelHandler和ChannelPipeline<ul><li>ChannelHandler - 事件的处理者，比如做一些业务代码处理</li><li>ChannelPipeline - 指定事件的处理顺序，先处理第一件事情，然后处理第二件事件, 换言之就是管理秩序.</li><li>ChannelHandlerContext - 用于ChannelHandler和ChannelPipeline之间的交互.</li></ul></li><li>ChannelFuture</li></ul></li><li><p>Netty的执行流程 - 这个流程的步数是奇数，首尾对称.</p><ul><li>启动服务</li><li>打开连接</li><li>接受数据</li><li>数据处理</li><li>发送数据</li><li>关闭连接</li><li>关闭服务</li></ul></li><li><p>Netty逻辑架构图<br>  <img src="/images/2020/netty-logic-diagram.png"></p></li><li><p>Netty功能图<br>  <img src="/images/2020/netty-function-diagram.png"></p></li></ul><h2 id="Netty的实践"><a href="#Netty的实践" class="headerlink" title="Netty的实践"></a>Netty的实践</h2><h2 id="网络编程框架的未来"><a href="#网络编程框架的未来" class="headerlink" title="网络编程框架的未来"></a>网络编程框架的未来</h2>]]>
    </content>
    <id>https://blog.yangricky.com/2020/10/12/netty/</id>
    <link href="https://blog.yangricky.com/2020/10/12/netty/"/>
    <published>2020-10-12T14:36:52.000Z</published>
    <summary>
      <![CDATA[<p>万物互联是现在乃至未来的趋势. 只要互联就需要网络，不管是无线还是有线。只要牵涉到联网，就牵涉到两台机器上的应用程序之间的通信。 只要是通信，我们就需要又快又好的进行通信。那么应该有一个事物来做这样基础的事情，这样开发者可以专注于写业务逻辑.<br>这个事物就是Netty. 当然Netty不是进行Java进行网络通信的唯一选择，我们自己也可以写一个网络编程框架,但在此刻，它是网络通信框架领域那颗最耀眼的明星,这就是影响力，必须承认它的人气。当我们讨论Java网络编程框架的时候，Netty是一个绕不过去的话题. 让我来沏一杯茶，闲言碎语几句.</p>
<ul>
<li>背景</li>
<li>什么是Netty?</li>
<li>为什么是Netty?</li>
<li>Netty是如何实现的？</li>
<li>Netty的实践</li>
<li>网络编程框架的将来</li>
</ul>]]>
    </summary>
    <title>Java之飞鸽传书 - Netty</title>
    <updated>2024-02-26T15:09:46.518Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="高性能" scheme="https://blog.yangricky.com/categories/%E9%AB%98%E6%80%A7%E8%83%BD/"/>
    <category term="高性能" scheme="https://blog.yangricky.com/tags/%E9%AB%98%E6%80%A7%E8%83%BD/"/>
    <content>
      <![CDATA[<p>要提高服务器的性能，正常的思路是</p><ul><li>单机堆硬件 - 加内存，换CPU</li><li>多个单机组成集群</li></ul><span id="more"></span><p>以上两种思路都是从硬件层面出发的。 我们这里讨论的当有大量请求过来的时候，不从硬件出发，而是从方法论出发，看看有哪些策略可以提升服务器的性能。</p><ul><li>两种维度</li><li>PPC</li><li>TPC</li><li>Reactor</li><li>Proactor</li></ul><p><img src="/images/2020/jump.jpeg"></p><h2 id="两种维度组合的场景"><a href="#两种维度组合的场景" class="headerlink" title="两种维度组合的场景"></a>两种维度组合的场景</h2><ul><li>连接数量多，请求数量多<ul><li>秒杀，双十一之类的业务，这类业务一定走分布式这条路</li></ul></li><li>连接数量多，请求数量少<ul><li>企业的官网</li></ul></li><li>连接数量少，请求数量多<ul><li>一些中间件，比如数据库连接池，一个数据库连接，是执行SQL的请求可能有几百个</li></ul></li><li>连接数量少，请求数量少<ul><li>企业的内部系统，比如我司创建学生账号的工具，查看学生学习情况的报表系统.</li></ul></li></ul><h2 id="PPC"><a href="#PPC" class="headerlink" title="PPC"></a>PPC</h2><p>PPC（Process Per Connection) 顾名思义就是一个连接一个进程。这种模式适合连接数不是特别的情况，比如数据库连接数.<br>缺点是</p><ul><li>Fork进程是有代价的</li><li>父子进程通信比较复杂</li></ul><h2 id="TPC"><a href="#TPC" class="headerlink" title="TPC"></a>TPC</h2><p><img src="/images/2020/tpc.png"><br>TPC (Thread Per Connection) 就是一个连接一个线程. 这种模式可以说是PPC的加强版. 它不需要fork进程，也没有父子进程通信复杂的情况. 但它来来额外的副作用. 这也属于BIO</p><ul><li>线程切换是需要代价的</li><li>线程之间协作不当就有可能出现死锁</li></ul><p>所以这是典型的发明了一个方案，然后制造了一个问题啊.</p><h2 id="Reactor"><a href="#Reactor" class="headerlink" title="Reactor"></a>Reactor</h2><p>Reactor在前面的博文有隐约提到过，它主要干了两件事</p><ul><li>将线程分为两类，一类主线程专门用来接受socket的请求，一类工作线程专门用来处理socket请求</li><li>主线程通过某种方式(事件驱动)将请求传给工作线程 - 好莱坞原则</li></ul><p>Reactor本质上是同步阻塞I&#x2F;O</p><h2 id="Proactor"><a href="#Proactor" class="headerlink" title="Proactor"></a>Proactor</h2>]]>
    </content>
    <id>https://blog.yangricky.com/2020/10/11/performance-1-pc/</id>
    <link href="https://blog.yangricky.com/2020/10/11/performance-1-pc/"/>
    <published>2020-10-11T15:11:07.000Z</published>
    <summary>
      <![CDATA[<p>要提高服务器的性能，正常的思路是</p>
<ul>
<li>单机堆硬件 - 加内存，换CPU</li>
<li>多个单机组成集群</li>
</ul>]]>
    </summary>
    <title>挑战个体极限 - 如何提高一台服务器的性能?</title>
    <updated>2024-02-26T15:09:46.518Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="分布式" scheme="https://blog.yangricky.com/categories/%E5%88%86%E5%B8%83%E5%BC%8F/"/>
    <category term="分布式" scheme="https://blog.yangricky.com/tags/%E5%88%86%E5%B8%83%E5%BC%8F/"/>
    <content>
      <![CDATA[<p>一个人抗一个木头会很重，如果两个人一起抗木头，两个人承担的分量会少点，如果三个人呢？那么三个人承担的分量会更少. 换言之，如果从整体上看，三个人的力量要大于两个人<br>现在的应用的数据变得越来越大，业务的流量越来越多了，分布式成为了一个不可避免的趋势。<br>这篇文章主要谈谈</p><ul><li>背景</li><li>分布式系统的整体设计目标</li><li>分布式系统需要解决的具体问题</li></ul><span id="more"></span><p><img src="/images/2020/worker.jpg"></p><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>从一个简单的例子开始: 计算一个nxn的矩阵，可以有两种办法</p><ul><li>在一台机器上直接计算出来，把结果存放到数据库或者缓存中.</li><li>将矩阵划分为四个象限（每个象限为n&#x2F;2），然后这四个象限分别由四个节点独自处理，处理好的结果最好再后再汇总起来，存放在某个地方.</li></ul><p>第一种办法就是单机处理，第二种办法就是分布式处理。<br>第二种办法可以看作分布式处理的最简单模型， 它涉及了</p><ul><li>计算</li><li>存储</li><li>节点之间的通信 - 这里的节点可以是真实的物理机器，或者虚拟机或手机，或者世界上任意可计算的设备</li></ul><h2 id="分布式系统的整体设计目标"><a href="#分布式系统的整体设计目标" class="headerlink" title="分布式系统的整体设计目标"></a>分布式系统的整体设计目标</h2><p>从上面的例子，我们可以看到要计算一个矩阵，我们需要CPU计算，然后保存结果。把这个例子延伸到多台机器上，我们可能会面临如下问题:</p><ul><li>分布式计算</li><li>分布式存储</li><li>分布式通信 - 这是一个很自然而然的概念，毕竟不同的机器是需要通信的.</li><li>分布式资源池化 - 不同机器上的 GPU, 内存形成逻辑上性能更强的GPU, 内存.</li></ul><p>将上面这四个问题领域合成一个大问题就是： 在某种资源的基础上，通过某种通信方式，进行某种计算，把计算的结果存储在某个地方，然后对外提供一个服务。</p><blockquote><p>这个结论受极客时间上的某位专家的启发.</p></blockquote><p>本着站在巨人肩膀上的原则，将上面的结论和我之前提出的又快又好的指标进行整合</p><blockquote><p>分布式系统的整体目标和远景就是：如何又快又好的实现这个大问题？</p></blockquote><h2 id="分布式系统需要解决的具体问题"><a href="#分布式系统需要解决的具体问题" class="headerlink" title="分布式系统需要解决的具体问题"></a>分布式系统需要解决的具体问题</h2><h3 id="节点之间的通信问题"><a href="#节点之间的通信问题" class="headerlink" title="节点之间的通信问题"></a>节点之间的通信问题</h3><ul><li>直来直往 - 想要和谁说话，直接找那个人. 这就是RPC问题</li><li>专职中介 - 想和谁说话，找专门的中介就可以了。 这就是消息队列问题</li></ul><h3 id="一个集群中的节点管理和互相协作的问题"><a href="#一个集群中的节点管理和互相协作的问题" class="headerlink" title="一个集群中的节点管理和互相协作的问题"></a>一个集群中的节点管理和互相协作的问题</h3><ul><li>国不可一日无君 - 一个集群中的leader节点挂了，如何产生一个新的Leader节点, 这就是分布式选举问题.</li><li>共商国事 - 如何制定一个国家政策得到大家的认可？在分布式领域就是共识问题。</li><li>国家是有秩序的 - 文明有礼，排队买票. 一个集群中的资源，在某个时间窗口只允许特定节点访问，这就是分布式互斥问题.</li><li>有始有终 - 一个人做事最好不要半吊子，要么不做，要么完成。在分布式领域，这就是分布式事务问题.</li></ul><h3 id="分布式资源管理和负载调度"><a href="#分布式资源管理和负载调度" class="headerlink" title="分布式资源管理和负载调度"></a>分布式资源管理和负载调度</h3><ul><li>单体调度</li><li>两层调度</li><li>共享状态调度</li></ul><h3 id="分布式数据存储"><a href="#分布式数据存储" class="headerlink" title="分布式数据存储"></a>分布式数据存储</h3><ul><li>分布式缓存</li></ul><h3 id="分布式高可靠"><a href="#分布式高可靠" class="headerlink" title="分布式高可靠"></a>分布式高可靠</h3><ul><li>负载均衡</li><li>流量控制</li><li>故障隔离</li><li>故障恢复</li></ul>]]>
    </content>
    <id>https://blog.yangricky.com/2020/10/11/distributed-system/</id>
    <link href="https://blog.yangricky.com/2020/10/11/distributed-system/"/>
    <published>2020-10-11T11:25:39.000Z</published>
    <summary>
      <![CDATA[<p>一个人抗一个木头会很重，如果两个人一起抗木头，两个人承担的分量会少点，如果三个人呢？那么三个人承担的分量会更少. 换言之，如果从整体上看，三个人的力量要大于两个人<br>现在的应用的数据变得越来越大，业务的流量越来越多了，分布式成为了一个不可避免的趋势。<br>这篇文章主要谈谈</p>
<ul>
<li>背景</li>
<li>分布式系统的整体设计目标</li>
<li>分布式系统需要解决的具体问题</li>
</ul>]]>
    </summary>
    <title>人多力量大 - 分布式导言</title>
    <updated>2024-02-26T15:09:46.516Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="网络" scheme="https://blog.yangricky.com/tags/%E7%BD%91%E7%BB%9C/"/>
    <category term="I/O" scheme="https://blog.yangricky.com/tags/I-O/"/>
    <category term="操作系统" scheme="https://blog.yangricky.com/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    <content>
      <![CDATA[<p>I&#x2F;O顾名思义就是输入输出，I&#x2F;O设备可以指网卡，键盘，打印机等，在这里为了方便讨论，I&#x2F;O专门指网络设备.<br>之前也看过不少专门讲I&#x2F;O的帖子和文章，其中有不少帖子有误导的嫌疑，比如打一些不恰当的比喻，所以我有了一种想重新梳理一下I&#x2F;O模型的冲动。</p><ul><li>背景</li><li>一些预备知识</li><li>同步阻塞</li><li>同步非阻塞</li><li>I&#x2F;O多路复用 </li><li>异步非阻塞I&#x2F;O</li><li>其他</li><li>总结</li></ul><span id="more"></span><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>既然是模型，说明为了解决一个特定的问题. 那么I&#x2F;O模型是为了解决什么问题呢？首先排除网络传输问题，那么只能是网卡接受到数据之后，如何给程序消费这个问题了。<br>程序A想要请求网络数据，程序B也要请求网络数据，以此类推，很有很多程序干类似的事情。</p><p>一个程序请求网络数据，会牵涉到四方:</p><ul><li>程序本身</li><li>CPU</li><li>操作系统</li><li>内存</li></ul><p>根据之前提到的”又快又好”的指标，如果这四方都能在程序请求网络数据的过程中，让自身的效率达到最有，那么I&#x2F;O模型的目的就达到了<br>是的，这也是I&#x2F;O模型的终结目的.</p><p><img src="/images/2020/io.png"></p><h2 id="一些预备知识"><a href="#一些预备知识" class="headerlink" title="一些预备知识"></a>一些预备知识</h2><h3 id="用户空间和内核空间"><a href="#用户空间和内核空间" class="headerlink" title="用户空间和内核空间"></a>用户空间和内核空间</h3><p>  为了保证内核数据的安全，操作系统将寻址空间分为内核空间和用户空间.可以想象一下，一家公司组织去看文艺演出，前排坐的都是领导，后排坐的都是员工，前排的区域是核心区域.</p><h3 id="进程的阻塞"><a href="#进程的阻塞" class="headerlink" title="进程的阻塞"></a>进程的阻塞</h3><p>  一个进程在执行的过程中，需要等待某件事情的发生，才能继续执行，那么进程有两种选择</p><ul><li>占用CPU</li><li>不占用CPU</li></ul><p>  如果进程选择不占用CPU, 那么意味着进程被阻塞了，进入等待状态。<br>  如果进程自己等待某件事情的发生，又不释放CPU的使用权，那么这个进程是自私的。</p><h3 id="页缓存-Page-Cache"><a href="#页缓存-Page-Cache" class="headerlink" title="页缓存 - Page Cache"></a>页缓存 - Page Cache</h3><p>  Linux内核为文件提供了一个缓存，换言之， 从网卡进来的数据，先放到内核的缓存区。接下来的事情就很自然：数据会在在某个场合下拷贝到应用程序的地址空间.</p><h3 id="同步和异步的区别"><a href="#同步和异步的区别" class="headerlink" title="同步和异步的区别"></a>同步和异步的区别</h3><p>  要区分同步和异步，就选取一个好的视角，这个视角就是应用程序. 现在一个程序A, 程序B, 网络请求接口getUser，这个接口执行的时间耗时不确定.</p><ul><li>程序A 调用 getUser, 等待了t时间之后，得到了结果，然后继续执行剩下的逻辑, 这就是同步调用.</li><li>程序B 调用 getUser，继续执行剩下的逻辑，过t时间之后，以某种方式(回调函数)拿到了结果，这就是异步调用.</li></ul><p>  在这里，没有操作系统的概念，没有CPU的概念，没有内存的概念，可以看到这是一种调用方式，这种调用方式可以被框架来实现。<br>  而且可以看得出，同步调用符合开发者的认知习惯，因为代码的书写顺序就是代码的执行顺序，所以同步更人性化。默认情况下，我们写的代码就是同步的.</p><h2 id="同步阻塞"><a href="#同步阻塞" class="headerlink" title="同步阻塞"></a>同步阻塞</h2><p>同步阻塞就是一个进程在等待某件事情发生的时候，让自己进入等待状态，让出CPU的使用权，待数据</p><ul><li>从网卡到内核区</li><li>然后从内核区和程序地址空间<br>再唤醒进程。在这里可以看到进程做了两件事情</li><li>干等</li><li>让出了CPU<br>所以可以看到进程还是让自己充实点的，在等的期间可以干点别的事情。</li></ul><p>同步阻塞的优点 </p><blockquote><p>实时性好.符合用户的认知模型.</p></blockquote><p>同步阻塞的缺点 </p><blockquote><p>效率不高</p></blockquote><h2 id="同步非阻塞"><a href="#同步非阻塞" class="headerlink" title="同步非阻塞"></a>同步非阻塞</h2><p>同步非阻塞是同步阻塞的优化，也就是进程别干等了，干点别的事情. 但程序需要每隔一段时间轮训数据有没有达到内核区域，如果达到了内核区域，就将内核区域的数据拷贝到应用程序地址空间，在这拷贝的过程中，进程是被阻塞的.<br>所以非阻塞强调的是数据从网卡达到内核区这个过程.<br>同步非阻塞的优点</p><blockquote><p>进程不是纯粹干等了，可以去干点别的事情.</p></blockquote><p>同步非阻塞的缺点</p><blockquote><p>实时性差。因为需要通过轮询才能拿到数据，因为数据有可能在两次轮询间隔期间已经准备就绪了。轮询是有代价的，也需要消耗CPU</p></blockquote><h2 id="I-O多路复用"><a href="#I-O多路复用" class="headerlink" title="I&#x2F;O多路复用"></a>I&#x2F;O多路复用</h2><p>上面提到了同步非阻塞的缺点就是需要轮询CPU拿到结果，轮询是有代价的.<br>有两个进程采用同步非阻塞，那么两个进程都需要轮询，如果有n个进程呢？那么n个进程都需要轮询，这种效率是低下的。 如何改进这种效率呢？<br>一个可行的思路是让一个东西（暂且称为X）统一管理轮询。<br>统一管理是一种哲学，比如线程池是统一管理线程，连接池是统一管理连接.<br>Unix下面的select, poll, epoll就是做类似的事情的。</p><ul><li>select<ul><li>POSIX规定的</li><li>调用select函数之前需要将文件描述符从用户态拷贝到内核态.</li><li>调用过程是: 应用程序 -&gt; select(轮询) -&gt; I&#x2F;O数据，在应用程序拿到I&#x2F;O数据之前，应用程序一直是被select阻塞的，就这样看来，它不比同步阻塞调用高明.</li><li>切换一个角度：如果有好多应用程序，那么这个优势就很明显，读取数据由select统一管理. 所以select的特长是处理更多的连接.</li></ul></li><li>poll<ul><li>本质上和select是一样的，但是select的加强版</li><li>select的文件描述符列表是有限制的，而poll是没有限制的</li></ul></li><li>epoll - Linux特有的, 在Linux 2.6 引入.<ul><li>文件描述符放在内核的一个事件表中，这个事件表是基于红黑树的实现的.</li><li>基于事件驱动的I&#x2F;O机制，只关注有I&#x2F;O事情发生的文件描述符</li><li>相比与select&#x2F;poll, epoll性能更高</li><li>epoll的实现是基于Reactor模式</li></ul></li></ul><p>观察select, poll和epoll, 会有这样一条线索</p><ul><li>如何又快又好的管理文件描述符<ul><li>用一种高效的数据结构 - 红黑树</li><li>避免文件描述符在用户态和内核态之间的移动 </li><li>不主动去监控文件描述的变化，而是文件描述符有变化的时候，主动通知消费方 - 好莱坞原则</li></ul></li></ul><h2 id="异步非阻塞I-O"><a href="#异步非阻塞I-O" class="headerlink" title="异步非阻塞I&#x2F;O"></a>异步非阻塞I&#x2F;O</h2><p>回头来看看同步非阻塞I&#x2F;O的缺点</p><ul><li>进程需要轮询才能拿到结果</li><li>数据内核区拷贝到程序地址空间的过程中程序被阻塞了</li></ul><p>如果能避免上面那两个问题是不是就完美了？ 是的.<br>异步I&#x2F;O的宏观视角：</p><ul><li>程序A 调用 getUser， 继续执行剩下的逻辑, t时间之后，以某种方式（通常是回调）获取结果。</li></ul><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><h3 id="windows-IOCP"><a href="#windows-IOCP" class="headerlink" title="windows IOCP"></a>windows IOCP</h3><h3 id="libevent"><a href="#libevent" class="headerlink" title="libevent"></a>libevent</h3><p>  libevent 是一个基于事件驱动的异步I&#x2F;O库</p><h3 id="libuv"><a href="#libuv" class="headerlink" title="libuv"></a>libuv</h3><p>  libuv也是一个基于事件驱动的异步I&#x2F;O库，主要用在node.js上.<br>  在linux上, libuv是基于epoll.<br>  在windows上, libuv是基于IOCP</p><h3 id="Reactor-模式"><a href="#Reactor-模式" class="headerlink" title="Reactor 模式"></a>Reactor 模式</h3><p><img src="/images/2020/reactor.jpg"><br>Reactor的英文原意是核反应堆，一个核反应堆可以提供很强的能量.<br>Reactor是一种这样的模式，它要求主线程负责监听文件描述符是否有事件发生，有的话就将事情发送给工作线程. 所以这里可以看到几个特点:</p><ul><li>将线程分为两大职责，一种是管理者，一种工作者</li><li>管理者负责信息的收集，然后将信息分发给工作者。从实现的角度，体现了事件驱动。从原则的角度，体现了好莱坞原则.</li></ul><h3 id="Proactor-模式"><a href="#Proactor-模式" class="headerlink" title="Proactor 模式"></a>Proactor 模式</h3><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>同步和异步是类库，框架或者语言层面的事物，比如张三开发了一个类库对I&#x2F;O多路复用进行了封装，那么我们可以说这个类库支持异步I&#x2F;O.<br>阻塞和非阻塞是操作系统进程层面的事物。<br>Linux I&#x2F;O模型的发展历程围绕了两个要素</p><ul><li>让应用程序尽可能的多做事情</li><li>让CPU尽可能的充分利用</li></ul>]]>
    </content>
    <id>https://blog.yangricky.com/2020/10/09/linux-IO/</id>
    <link href="https://blog.yangricky.com/2020/10/09/linux-IO/"/>
    <published>2020-10-09T14:01:51.000Z</published>
    <summary>
      <![CDATA[<p>I&#x2F;O顾名思义就是输入输出，I&#x2F;O设备可以指网卡，键盘，打印机等，在这里为了方便讨论，I&#x2F;O专门指网络设备.<br>之前也看过不少专门讲I&#x2F;O的帖子和文章，其中有不少帖子有误导的嫌疑，比如打一些不恰当的比喻，所以我有了一种想重新梳理一下I&#x2F;O模型的冲动。</p>
<ul>
<li>背景</li>
<li>一些预备知识</li>
<li>同步阻塞</li>
<li>同步非阻塞</li>
<li>I&#x2F;O多路复用 </li>
<li>异步非阻塞I&#x2F;O</li>
<li>其他</li>
<li>总结</li>
</ul>]]>
    </summary>
    <title>Linux的I/O模型</title>
    <updated>2024-02-26T15:09:46.518Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="Java" scheme="https://blog.yangricky.com/tags/Java/"/>
    <category term="高并发" scheme="https://blog.yangricky.com/tags/%E9%AB%98%E5%B9%B6%E5%8F%91/"/>
    <category term="高性能" scheme="https://blog.yangricky.com/tags/%E9%AB%98%E6%80%A7%E8%83%BD/"/>
    <content>
      <![CDATA[<p>一个系统如果足够稳定，而且客户对它也很满意，我们一般没有去优化它的必要，但如果随着用户量增加，系统变的缓慢，我们就有优化它的必要性了. 借用我前面博文《一种可以衡量事物的指标-快和好》，一个系统追求的目标之一是快，但快的前提的是<br>好，也就是稳，否则步伐太大了就容易扯着蛋了. 优化系统是大的话题，这里只讨论其中的一个小领域-高并发.<br>任何一个计算机的问题，都能从现实中找到雏形和模型，高并发也不例外。高并发的内容还是比较多的，所以不同类型的高并发问题需要不同的角度，就好像作战的时候，战斗机需要在高空1万米，也需要突然下降到高空1千米，也有可能是潜艇下沉到水下几千米配合作战. 这种思维模型我给它取了个名字叫“海陆空”模型.</p><ul><li>什么是并发？</li><li>为什么需要并发？</li><li>如何使用并发？</li><li>并发的身影</li><li>并发的未来？</li></ul><p><img src="/images/2020/arrow.jpeg"></p><h2 id="什么是并发？"><a href="#什么是并发？" class="headerlink" title="什么是并发？"></a>什么是并发？</h2><h3 id="从现实的角度，并发像什么？"><a href="#从现实的角度，并发像什么？" class="headerlink" title="从现实的角度，并发像什么？"></a>从现实的角度，并发像什么？</h3><p>  在现实世界里，我们做事是有顺序的，比如做完了事情1，再做事情2, 也有可能是没有顺序的, 事情1和事情2是可以同时进行的. 所以我们在这里看到两个特征</p><ul><li>事情是可以拆解的</li><li>从人的观察者角度，在一个时间窗口(t0-t1)内，事情是同时进行的.<br>  在这里，我没有没有提到任何程序领域的术语，换句话说，上面的这两个特征在管理学是常见的，这两个特征可以认为是并发的雏形.<br>  本文仅仅讨论狭义上的并发：基于线程的多任务。</li></ul>  <span id="more"></span><h3 id="从编程的角度，并发要解决的核心问题是什么？"><a href="#从编程的角度，并发要解决的核心问题是什么？" class="headerlink" title="从编程的角度，并发要解决的核心问题是什么？"></a>从编程的角度，并发要解决的核心问题是什么？</h3><p>  并发要解决的核心问题是</p><ul><li>分解 - 如果要对一件事情进行并发处理，那么这件事情最起码是可以分解的, 如果这件事情不能分解，那么并发无从谈起.</li><li>协作 - 这个问题不具备必然性，仅仅发生在事情1和事件2有关系的前提下，比如事情2依赖事件1的结果</li><li>互斥 - 处理事件1和事件2的时候需要访问同一个资源，如果保证访问资源的时候只能自己访问，而且要保证系统的整体的利用率？<br>  以上三个问题可以说是层层递进的, 有了分解才有可能协作和互斥.<br>  这就是任何编程领域，并发要解决的所有问题种类吗？答案是肯定的.<br>  但有人可能很疑惑，并发不是会牵涉到什么自旋锁，可重入锁，分片还有什么volatile么？<br>  哦，不急，因为现在战斗机还在天上1万米的高空巡逻，遇到特定的问题和特定的目标慢慢会下降的.<br>  也可以想象这是一部小说，这部小说有三条主线，这三条主线最终会汇聚到一起的.</li></ul><p>  在开发者眼里，并发的目的和愿景是：用一种符合开发者认知模型的方式来解决上述三个问题. 换句话说，就是认知成本少点。 为什么这件事情很重要?</p><ul><li>我们开发软件是属于工程领域而不是科学领域，所以安全可控是首要目标，要是谁写个高并发，写的很炫，但很难维护，很难测试，最终会让人抓狂.</li><li>人是习惯的产物，就比如做西红柿炒蛋这个菜，我们知道放完鸡蛋会放西红柿，而不是黄瓜. 所以高并发的编写应该符合人的认知模型.</li></ul><h3 id="并发遇到的阻碍是什么？"><a href="#并发遇到的阻碍是什么？" class="headerlink" title="并发遇到的阻碍是什么？"></a>并发遇到的阻碍是什么？</h3><p>  并发要解决的问题都是有实际意义的事情，看起来很美好。就好比我要成为富豪一样，这件事情本身没有错，但容易吗？不容易，因为资源有限，能力有限.<br>  并发要落地，还得需要计算机来帮忙，所以得看看计算机能不能让我很容易的达到并发这个目标，最好是我说一句话就能达到并发的目的.<br>  一个基本事实：CPU, 内存, I&#x2F;O的速度不匹配. 根据木桶原理，一个系统的瓶颈取决于最短的那块木板.</p><blockquote><p>姚明在休斯敦的时候，战术基本上是围绕着姚明来打造的，后来麦迪加盟火箭，管理层对麦迪的要求是球队的大战略是必须等姚明落位之后才能开始进攻，麦迪只能同意这种战略。火箭的慢的原因是在于姚明。无奈啊，黄种人跑不动。</p></blockquote><p>  现在基本事实已经确定了，可以说这些基本事实是原罪，是障碍的起点。可能有人要问，为什么不能把 CPU, 内存, I&#x2F;O的速度弄成一样呢？这个从硬件上来说真不好弄.<br>  既然从硬件层面上解决不了这个问题，那就从设计和策略的角度上来解决. 计算机系统的参与方都开始大展拳脚.<br>  一个重要的规律： 任何的方案有好处，也有代价，这个代价可以理解为成本或者副作用，应了那句天下没有免费的午餐.</p><ul><li>计算机体系结构的策略 - 增加 CPU 缓存，平衡CPU和内存之间的速度差异<ul><li>在单核时代，只有一个CPU缓存，CPU缓存和内存之间的一致性很容易解决。一个线程更改了CPU的缓存，另外一个线程是可以知道这个CPU缓存被更改了的. 这个知道就叫做“可见性”.</li><li>在多核时代, 有多个CPU缓存，线程1操作 CPU1缓存，线程2有可能操作的是CPU2缓存。这个时候线程2就不知道线程1的操作结果了.</li></ul></li><li>操作系统的策略 - 增加进程，线程，平衡 CPU和 I&#x2F;O之间的速度差异<ul><li>我们之所以能一边听音乐，一边发微信，是因为操作系统多进程的功劳，换句话说操作系统定义了时间片这个概念，比如在最开始的10毫秒把CPU的资源分给进程A,接下来的10毫秒把CPU的资源分给进程B, 这样能充分利用CPU资源(充分利用资源这是一种政治正确，谁会鼓励说浪费资源呢？)，给用户的感觉就是同时做两件事情.</li><li>正如去医院看病的时候，每个人都拿了一些号，叫到张三，张三就进去会诊，在会诊的时候，医生要让张三去抽血，才能得出完整的会诊结论，在张三出去抽血的时候，医生在干嘛呢？在等张三吗？显然不是. 可继续为李四会诊，等张三抽血完毕，再为张三会诊. 在这里可以看到, CPU和医生的角色有点类似.</li><li>但这样的策略会带来的一些问题，从操作的角度来说，会有任务切换这个概念. 举个例子, count &#x3D; count + 1 是语言层面的代码，这个代码在CPU的角度看来是三个指令, 所谓的任务切换是CPU指令级别的切换，而不是语言层面的切换. 所以我们希望能有一个策略保证任务切换的效果是基于语言层面.</li></ul></li><li>编译器的策略 - 优化指令的执行顺序<ul><li>编译器会对一些代码的执行顺序进行优化, “a&#x3D;6；b&#x3D;7”优化之后可能变为”b&#x3D;7;a&#x3D;6;”, 这个代码看上去正常, 但在某些例子上却有意想不到的情况，比如双重加锁的单例模式，会引发空指针异常.</li></ul></li></ul><h2 id="为什么需要并发？"><a href="#为什么需要并发？" class="headerlink" title="为什么需要并发？"></a>为什么需要并发？</h2><p>  系统是要追求快，在快的基础上追求好. 换一种说法，如何让系统在单位时间内执行更多的任务?</p><h2 id="如何使用并发？"><a href="#如何使用并发？" class="headerlink" title="如何使用并发？"></a>如何使用并发？</h2><h3 id="整体设计原则"><a href="#整体设计原则" class="headerlink" title="整体设计原则"></a>整体设计原则</h3><p>  大体有四个原则，层层递进, 不局限于Java, 是跨语言的, 这里的原则也可以类比一个国家的宪法或者党的纲领.</p><ol><li>不要使用并发，如果要使用高并发, 必须要有很强的理由.</li><li>如果确实要使用并发，尽可能不要有共享变量</li><li>如果确实需要共享变量，那么尽可能保证变量是只读的</li><li>如果不能保证变量是只读的，那么要确保变量访问的时候要同步。基本上就是讨论各种各样的锁.</li></ol><h3 id="整体设计策略"><a href="#整体设计策略" class="headerlink" title="整体设计策略"></a>整体设计策略</h3><p>  这里的设计策略存在的前提是使用了高并发，也是应用了上面的原则2, 原则3, 原则4.</p><ul><li>分解</li><li>同步</li><li>互斥<br>  这里的整体设计策略和前面提到的并发要解决的核心问题是一致的, 这也是跨语言的</li></ul><h3 id="整体设计模式"><a href="#整体设计模式" class="headerlink" title="整体设计模式"></a>整体设计模式</h3><ul><li>Immutability</li><li>Copy-on-Write</li><li>ThreadLocal</li><li>Guarded Suspension</li><li>Balking</li><li>Thread-Per-Message</li><li>Worker Thread</li><li>两阶段终止</li><li>生产者消费者</li><li>Actor</li><li>软件事务内存</li><li>协程</li><li>CSP</li></ul><h3 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h3><ul><li>Java是如何解决并发路上的障碍的？<ul><li>可见性和顺序问题<ul><li>final<ul><li>表明这个变量是不可变的</li></ul></li><li>volatile<ul><li>volatile int x &#x3D; 0, 告诉编译器对x的读写不要用CPU缓存</li><li>在JDK1.5以前，忽略了多核的情况，在JDK1.5对volatile进行了增强.</li></ul></li><li>synchronized</li><li>8个Happens-Before原则<ul><li>程序的顺序性规则</li><li>Volatile变量规则</li><li>传递性</li><li>管程中锁的规则 - 对一个锁的解锁 Happens-Before 于后续对这个锁的加锁</li><li>线程Start规则</li><li>线程Join规则</li><li>线程中断规则</li><li>对象终结规则</li></ul></li><li>小结<ul><li>可见性和顺序性问题主要由Java内存模型的规范来解决</li><li>A Happens-Before B 意味这 A做了某件事情B是知道的。</li></ul></li></ul></li><li>原子性问题<ul><li>原子性问题是由CPU线程切换引起的，所以禁止CPU线程切换是一个很自然的思路，要禁止CPU线程切换就要禁止中断，但禁止中断这种做法只在单核CPU上有效</li><li>互斥就是要保证同一时刻只有一个线程执行<ul><li>synchronized是Java对管程的一种实现<ul><li>一把锁可以锁住多个资源</li></ul></li></ul></li></ul></li></ul></li><li>如何实现等待通知机制?<ul><li>synchronized 配合 wait()、notify()、notifyAll()</li></ul></li><li>Lock和Condition</li><li>Semaphore</li><li>ReadWriteLock</li><li>StampedLock</li><li>CountDownLatch和CyclicBarrier</li></ul><h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><ul><li>锁<ul><li>一把锁可以锁住多个资源<ul><li>资源和资源之间有协作关系</li></ul></li><li>用不同的锁对受保护资源进行精细化管理，能够提升性能，可能的代价就是死锁。<ul><li>资源和资源之间没有协作关系</li></ul></li><li>多把锁不可以锁住一个资源</li><li>用锁两大要素：锁定的对象和锁定的资源.</li></ul></li></ul><h2 id="并发的身影"><a href="#并发的身影" class="headerlink" title="并发的身影"></a>并发的身影</h2><h2 id="并发的未来"><a href="#并发的未来" class="headerlink" title="并发的未来"></a>并发的未来</h2>]]>
    </content>
    <id>https://blog.yangricky.com/2020/10/06/concurrent/</id>
    <link href="https://blog.yangricky.com/2020/10/06/concurrent/"/>
    <published>2020-10-06T15:33:47.000Z</published>
    <summary>
      <![CDATA[<p>一个系统如果足够稳定，而且客户对它也很满意，我们一般没有去优化它的必要，但如果随着用户量增加，系统变的缓慢，我们就有优化它的必要性了. 借用我前面博文《一种可以衡量事物的指标-快和好》，一个系统追求的目标之一是快，但快的前提的是<br>好，也就是稳，否则步伐太大了就容易扯着蛋了. 优化系统是大的话题，这里只讨论其中的一个小领域-高并发.<br>任何一个计算机的问题，都能从现实中找到雏形和模型，高并发也不例外。高并发的内容还是比较多的，所以不同类型的高并发问题需要不同的角度，就好像作战的时候，战斗机需要在高空1万米，也需要突然下降到高空1千米，也有可能是潜艇下沉到水下几千米配合作战. 这种思维模型我给它取了个名字叫“海陆空”模型.</p>
<ul>
<li>什么是并发？</li>
<li>为什么需要并发？</li>
<li>如何使用并发？</li>
<li>并发的身影</li>
<li>并发的未来？</li>
</ul>
<p><img src="/images/2020/arrow.jpeg"></p>
<h2 id="什么是并发？"><a href="#什么是并发？" class="headerlink" title="什么是并发？"></a>什么是并发？</h2><h3 id="从现实的角度，并发像什么？"><a href="#从现实的角度，并发像什么？" class="headerlink" title="从现实的角度，并发像什么？"></a>从现实的角度，并发像什么？</h3><p>  在现实世界里，我们做事是有顺序的，比如做完了事情1，再做事情2, 也有可能是没有顺序的, 事情1和事情2是可以同时进行的. 所以我们在这里看到两个特征</p>
<ul>
<li>事情是可以拆解的</li>
<li>从人的观察者角度，在一个时间窗口(t0-t1)内，事情是同时进行的.<br>  在这里，我没有没有提到任何程序领域的术语，换句话说，上面的这两个特征在管理学是常见的，这两个特征可以认为是并发的雏形.<br>  本文仅仅讨论狭义上的并发：基于线程的多任务。</li>
</ul>]]>
    </summary>
    <title>Java之离弦之箭 - 高并发</title>
    <updated>2024-02-26T15:09:46.516Z</updated>
  </entry>
  <entry>
    <author>
      <name>迷途书童</name>
    </author>
    <category term="Java" scheme="https://blog.yangricky.com/categories/Java/"/>
    <category term="Java" scheme="https://blog.yangricky.com/tags/Java/"/>
    <category term="JVM" scheme="https://blog.yangricky.com/tags/JVM/"/>
    <content>
      <![CDATA[<p>一直以来，JVM是Java开发者进阶过程必然要遇到的一块知识点。JVM离绝大多数开发者的实际工作还是比较远的，让人感觉是一个华而不实的东西。或者说有某一类群体，学习JVM就是为了面试，而且是以机械背诵的方式来学习JVM的，这都已经背离了学习一个知识的初衷.</p><ul><li>什么是JVM</li><li>JVM主要解决了哪些问题？</li><li>JVM点滴</li></ul><span id="more"></span><h2 id="什么是JVM"><a href="#什么是JVM" class="headerlink" title="什么是JVM"></a>什么是JVM</h2><p>JVM是一种将高级语言转位机器码的一种工具，目前支持的语言有Java, Clojure, Kotlin, Scala, Groovy.<br>当从整体上讨论JVM的时候，有两种角度</p><ul><li>JVM规范</li><li>JVM实现</li></ul><p>在Java的世界里, JVM主要干了两件事（本文以下的讨论都以Java为基础）</p><ul><li>将Java文件转为Class文件</li><li>将Class文件转为机器码</li></ul><p>这两件事描述起来很简单，但从实践角度来说，有很多的细节和技巧，根据我前面提到过的“又快又好”的指标，JVM的目标是如何又快又好的干这两件事.</p><h2 id="JVM主要解决了哪些问题"><a href="#JVM主要解决了哪些问题" class="headerlink" title="JVM主要解决了哪些问题?"></a>JVM主要解决了哪些问题?</h2><p>JVM要解决Java开发过程中遇到的所有问题，比如泛型，异常，基本类型等等，根据二八法则，这里我只讨论一些大的问题，其余琐碎的问题以后单独再聊.</p><h3 id="内存的分配"><a href="#内存的分配" class="headerlink" title="内存的分配"></a>内存的分配</h3><p>这个问题是个通用的问题，可以简单的描述为：如何将有限的资源合理的分配给各种各样的消费者？或者这个问题接近于现实中的情况：如何将土地和财富分配给社会各阶层的人？<br><img src="/images/2020/jvm-memory.png"><br>所以内存分配的第一个问题，就是要搞清楚: Java里面占用内存资源的有哪几类要素？如图所示</p><ul><li>线程共享<ul><li>堆<ul><li>存放new出来的对象</li><li>数组</li></ul></li><li>方法区<ul><li>类信息<ul><li>class文件常量池</li></ul></li><li>静态变量</li><li>常量信息</li></ul></li></ul></li><li>线程不共享<ul><li>程序计数器<ul><li>程序计数器是记录的字节码的地址信息。每个线程都有自己的程序计数器，当线程的CPU时间片耗尽挂起之后，需要记录字节码的执行位置，当再次获取到时间片之后，可以从上次的位置继续执行。这个功能是必要的，好比我们看完书的一段章节之后，会将这一页折一下或者用笔做个标注.</li><li>执行Native方法的时候，程序计数器没有值，因为本地方法不是基于字节码来实现的.</li></ul></li><li>虚拟机栈<ul><li>符合栈的特性：先进后出</li><li>栈帧 - 方法级别的概念，简单来说，方法执行之前栈帧入栈，方法执行完毕，栈帧出栈。</li><li>栈顶的元素通常称为当前栈帧，里面与之相关的方法称之为当前方法.</li></ul></li><li>本地方法栈 - 和虚拟机栈大体类似，但处理的对象不同，本地方法栈处理的是本地方法<ul><li>没有栈帧的概念</li></ul></li></ul></li></ul><h3 id="内存的回收"><a href="#内存的回收" class="headerlink" title="内存的回收"></a>内存的回收</h3><p>一个消费者资源使用完了，就需要回收，以便给其他消费者使用。</p><!-- TODO: 这个话题升级为垃圾回收，顺便思考一下其他语言. JVM知识体系里必须要掌握的事情 --><h3 id="锁"><a href="#锁" class="headerlink" title="锁"></a>锁</h3><!-- TODO: 这个话题升级为高并发，注意内存模型这个名词，可能有点疑惑 --><h3 id="类文件是如何加载的"><a href="#类文件是如何加载的" class="headerlink" title="类文件是如何加载的"></a>类文件是如何加载的</h3><!-- TODO: 类加载的高层次逻辑是什么样的？ --><!-- TODO: 为什么在需要有类加载这件事情，而.net的世界里没有这回事？JVM只是体系里必须要掌握的事情 --><h2 id="JVM-点滴"><a href="#JVM-点滴" class="headerlink" title="JVM 点滴"></a>JVM 点滴</h2>]]>
    </content>
    <id>https://blog.yangricky.com/2020/09/30/jvm/</id>
    <link href="https://blog.yangricky.com/2020/09/30/jvm/"/>
    <published>2020-09-30T02:25:43.000Z</published>
    <summary>
      <![CDATA[<p>一直以来，JVM是Java开发者进阶过程必然要遇到的一块知识点。JVM离绝大多数开发者的实际工作还是比较远的，让人感觉是一个华而不实的东西。或者说有某一类群体，学习JVM就是为了面试，而且是以机械背诵的方式来学习JVM的，这都已经背离了学习一个知识的初衷.</p>
<ul>
<li>什么是JVM</li>
<li>JVM主要解决了哪些问题？</li>
<li>JVM点滴</li>
</ul>]]>
    </summary>
    <title>Java之黑暗森林 - JVM</title>
    <updated>2024-02-26T15:09:46.517Z</updated>
  </entry>
</feed>
