<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>高家祺的博客</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://challange.github.io/"/>
  <updated>2019-06-23T09:24:40.358Z</updated>
  <id>https://challange.github.io/</id>
  
  <author>
    <name>高家祺</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>哲学点亮创新：柏拉图理念论（破界）</title>
    <link href="https://challange.github.io/%E6%B7%B7%E6%B2%8C-%E5%93%B2%E5%AD%A6%E7%82%B9%E4%BA%AE%E5%88%9B%E6%96%B0%EF%BC%9A%E6%9F%8F%E6%8B%89%E5%9B%BE%E7%90%86%E5%BF%B5%E8%AE%BA/"/>
    <id>https://challange.github.io/混沌-哲学点亮创新：柏拉图理念论/</id>
    <published>2019-06-22T14:05:05.000Z</published>
    <updated>2019-06-23T09:24:40.358Z</updated>
    
    <content type="html"><![CDATA[<h3 id="第一性原理（UNIFICATION）"><a href="#第一性原理（UNIFICATION）" class="headerlink" title="第一性原理（UNIFICATION）"></a>第一性原理（UNIFICATION）</h3><blockquote><p>如何建立一个系统？第一性原理<br>如何建立系统的第一性原理？公理化方法</p></blockquote><p>关键词：认知边界、从众效应、群体信念黑洞<br>任何时候我们都是被关进自己认知框架的囚徒</p><p>认知升级才能打破认知边界，打破认知边界就是破界创新。<br>❔认知升级有什么意义？——降低认知熵增。<br>1.感性思维——无限经验信息<br>2.理性思维——有限思维模型<br>3.思维逻辑——三种逻辑形式<br>4.第一性原理——简一律元起点</p><p>（1）归纳法<br>归纳法不能保真。即使前提正确，也不能保证结论一定正确。<br>实践在先，实践引导实践——技艺思维：速度快，但不可迁移<br>同一通道之内的增长，连续性，归纳法最有效率；<br>（2）演绎法<br>演绎法能够保真。如果前提是正确的，那么结论一定正确。<br>逻辑假设在先，假设引导实践——哲科思维：速度慢，但可迁移<br>转换到另一个通道，非连续性，必须运用演绎法。</p><p>归纳法谬误：归纳法默认增加了一个条件——连续性，当遇到非连续性节点，便会出现错误。即使所有前提都正确，结论依然有可能错误。因此归纳法只能证伪，不能证明。<br>演绎法可以解决非连续性问题，演绎法能够保真，如果<strong>前提</strong>正确，那么结论一定正确。</p><p>第一性原理<br>任何一个系统都有自己的第一性原理<br><img src="/images/哲学点亮创新：柏拉图理念论/1561269916091.png" alt="Alt text"><br>第一性原理建立在一条或几条逻辑奇点之上，逻辑奇点是不证自明，或是更大系统的第一性原理。</p><p>逻辑奇点+公理化方法 =&gt; 第一性原理<br><img src="/images/哲学点亮创新：柏拉图理念论/1561270205169.png" alt="Alt text"><br><img src="/images/哲学点亮创新：柏拉图理念论/1561270253716.png" alt="Alt text"><br>破界创新，用第一性原理的思维方式去发现更大尺度的第一性原理</p><p>公理化方法<br><img src="/images/哲学点亮创新：柏拉图理念论/1561270425268.png" alt="Alt text"><br><img src="/images/哲学点亮创新：柏拉图理念论/1561270440610.png" alt="Alt text"></p><p>每一个系统有自己的第一行原理：但系统是有层级之分（大系统和子系统），因此不同系统的第一性原理之间也有层次关系。每个系统都有自己的适用范围，第一性原理随之也有自己的作用范围，几乎没有任何一个第一性原理是绝对普世通用的。<br>第一性原理是系统之内的，逻辑奇点是系统之外的。</p><h3 id="破界创新"><a href="#破界创新" class="headerlink" title="破界创新"></a>破界创新</h3><blockquote><p>如何打破一个系统？第一性原理<br>如何打破系统的第一性原理？奇点下移</p></blockquote><p>什么叫逻辑起点的下移？既有了一个新的逻辑奇点，可以解释原来不可解释的逻辑奇点</p><p><strong>破界创新三步曲：</strong></p><ol><li>打破隐含假设</li><li>重构基石假设</li><li>洞见第一原理</li></ol><p>破界创新的难点和重点在于：发现和打破<strong>隐含假设</strong><br>案例：欧氏几何有一个重要的隐含假设：平面空间</p><p>破：发现反常，逻辑奇点下移，发现更大系统<br>立：基于新的逻辑奇点（公理），推出全新系统</p><p>用第一性原理的思维方式去发现更大尺度的第一性原理。</p><p>所谓破解，不是破外在事业的边界，而是破内在认知的边界</p><p>创始人的认知边界，是一个公司真正的边界</p><p>反共识</p><h3 id="理念世界"><a href="#理念世界" class="headerlink" title="理念世界"></a>理念世界</h3><blockquote><p>洞见更大尺度的第一性原理的关键步骤：如何构建可靠的基石假设？</p></blockquote><p>重要的不是独立的事物，而是<strong>事物之间的组合关系</strong> </p><p>第一性原理存在于现实世界，理念世界超越现实世界<br><img src="/images/哲学点亮创新：柏拉图理念论/IMG_3332.JPG" alt="Alt text"></p><p><strong>打破隐含假设的方法</strong><br>是否符合逻辑三洽：自洽、它洽、续洽</p><p>重构，重写定义概念</p><p>什么是理性精神？<br>理念世界 —&gt; 自然规律 —&gt; 指导实践</p><p>理念世界上独立的真实存在，理念世界的法则优先于现实世界</p><h3 id="创新哲学"><a href="#创新哲学" class="headerlink" title="创新哲学"></a>创新哲学</h3><p>爱因斯坦重写定义科学<br>常规科学：实践实验 —&gt; 物理定律 —&gt; 理念世界<br>爱因斯坦：理念世界 —&gt; 物理定律 —&gt; 实践实验 </p><p>传统创新：实践经验 —&gt; 创新理论 —&gt; 哲科思维<br>创新哲学：哲科思维 —&gt; 创新理论 —&gt; 指导实践</p><p>运用第一性原理，发现<strong>使命</strong><br>在不同的领域，反复应用相同的第一性原理。将同一种底层思维运用到所有领域</p><p>哲科思维点亮创新<br>物理学思维：还原论 —&gt; 组合创新<br>生物学思维：进化论 —&gt; 分形创新<br>复杂性思维：自组织 —&gt; 涌现创新<br>本体论哲学：理念论 —&gt; 破界创新</p><p>芒格：重要学科的重要理论</p><ol><li>数学的复利模型</li><li>物理学的临界质量模型</li><li>生物学的现代综合进化论</li><li>化学的自我催化模型</li><li>工程学的冗余备份模型</li><li>心理学的人类误判心理学</li></ol><p>跨学科的迁移学习</p><p>要想更好地投资，必须更深刻地理解世界——芒格</p><p>混沌味道：追索问题背后的为什么？</p><p>过程拆解为阶段<br>拆解关键要素</p><p>将知识拆解为基本原理，接着在新的领域重构基本原理，</p><p>懒蚂蚁效应</p><p>面向未来的创新者=科学家+哲学家+企业家<br>面向未来的创新能力=科学思考+哲学思考+商业思考</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;第一性原理（UNIFICATION）&quot;&gt;&lt;a href=&quot;#第一性原理（UNIFICATION）&quot; class=&quot;headerlink&quot; title=&quot;第一性原理（UNIFICATION）&quot;&gt;&lt;/a&gt;第一性原理（UNIFICATION）&lt;/h3&gt;&lt;blockquo
      
    
    </summary>
    
      <category term="程序人生" scheme="https://challange.github.io/categories/%E7%A8%8B%E5%BA%8F%E4%BA%BA%E7%94%9F/"/>
    
    
      <category term="程序人生" scheme="https://challange.github.io/tags/%E7%A8%8B%E5%BA%8F%E4%BA%BA%E7%94%9F/"/>
    
  </entry>
  
  <entry>
    <title>科学点亮创新：达尔进化论（生命）</title>
    <link href="https://challange.github.io/%E6%B7%B7%E6%B2%8C-%E7%A7%91%E5%AD%A6%E7%82%B9%E4%BA%AE%E5%88%9B%E6%96%B0%EF%BC%9A%E8%BE%BE%E5%B0%94%E8%BF%9B%E5%8C%96%E8%AE%BA/"/>
    <id>https://challange.github.io/混沌-科学点亮创新：达尔进化论/</id>
    <published>2019-06-22T14:05:05.000Z</published>
    <updated>2019-06-23T09:22:22.212Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>创新是发展的第一动力因，是增长的最佳引擎。面对越来越多的不确定性和非连续性，该如何培育创新物种？如何打破认知边界？如何实现持续创新？<br>日渐消失的增长红利（人口红利、互联网红利、全球化红利），红利消失，增长还是要靠<strong>创新</strong></p></blockquote><h3 id="进化算法：如何进行战略取舍，让创新自我进化？"><a href="#进化算法：如何进行战略取舍，让创新自我进化？" class="headerlink" title="进化算法：如何进行战略取舍，让创新自我进化？"></a>进化算法：如何进行战略取舍，让创新自我进化？</h3><blockquote><p>自然界无飞跃，创新无飞跃</p></blockquote><p><img src="/images/科学点亮创新：达尔进化论/1561172992715.png" alt="Alt text"></p><p><img src="/images/科学点亮创新：达尔进化论/1561173684819.png" alt="Alt text"></p><p>进化算法适合以下三件事</p><ol><li>如何到达你想去又找不到路的地方</li><li>如何到达你无法想象的领域</li><li>如何开辟全新领域</li></ol><p><img src="/images/科学点亮创新：达尔进化论/1561173876366.png" alt="Alt text"></p><h5 id="变异——个体差异：（自上而下、多样探索）"><a href="#变异——个体差异：（自上而下、多样探索）" class="headerlink" title="变异——个体差异：（自上而下、多样探索）"></a>变异——个体差异：（自上而下、多样探索）</h5><p>——企业需要有足够丰富的多样性，多样性是创新的来源。除非在系统中注入多样性，否则内生增长将彻底消失。<br>多样性的来源：</p><ol><li>突变：十倍速变化的单一要素</li><li>孵化：培育内部多样性创新源</li><li>杂交：人工干预引入多样育种<br>而现实中，人们常常会忽略淘汰变异个体，这个成熟大公司缺乏创新的原因</li></ol><h5 id="选择——生存竞争：一二曲线、自我破坏"><a href="#选择——生存竞争：一二曲线、自我破坏" class="headerlink" title="选择——生存竞争：一二曲线、自我破坏"></a>选择——生存竞争：一二曲线、自我破坏</h5><p>进化论的基石：遗传变异、自然选择</p><p>生存竞争的主角不是物种之间的竞争，而是同一物种的个体之间的竞争<br>有利于生存的变异会被选择下来，不利于生存的变异会被淘汰，这就是自然选择。<br>更为究竟的说：自然只会淘汰不利于生存的变异，有利于生存的变异被剩下来而不是被主动选择。<br>从个体差异推导出遗传变异，从生存竞争推导出自然选择，遗传变异和自然选择共同推导出达尔文的第一性原理：进化论。<br>达尔文的根本思想：存在没有设计师的设计</p><p>创新的过程往往就是新组合对旧组合通过竞争热加以消灭的过程。熊彼特把这一过程称为“<strong>创造性破坏</strong>”。<br>市场经济就是进化经济，它的核心精神是：<strong>想生就生，该死就死</strong><br><img src="/images/科学点亮创新：达尔进化论/1561178554415.png" alt="Alt text"><br>大哥吃小弟<br><img src="/images/科学点亮创新：达尔进化论/1561178607959.png" alt="Alt text"></p><p><strong>只有攻击自己才能成为不死鸟</strong></p><p>企业和业务不是一回事，业务只是企业的子系统。反脆弱中所说，只有破坏子系统才能让母系统长存。</p><p>市场的创造性破坏，对于企业增长有什么启示呢？<br>答：将市场的创造性破坏模式引入到企业中去！<br>问题1:我们都希望第一曲线基业长青，但是任何曲线豆无法避免极限点，这时候怎么办？<br>答：一次又一次地跨越第二条曲线。<br>问题2:什么时候启动第二曲线？<br>答：第一曲线过了破局点；第一曲线还在增长，但增长加速度开始下降；deadline:第一曲线到达财务极限点之前。<br><img src="/images/科学点亮创新：达尔进化论/1561178752780.png" alt="Alt text"></p><p>问题3:如果第二曲线已经过了破局点，而且和第一曲线之间形成了竞争，该如何选择？<br>答：创造性自我破坏。像市场淘汰过气企业一样，企业要破坏自己的过气业务和产品。</p><h5 id="隔离：错位竞争、边缘创新"><a href="#隔离：错位竞争、边缘创新" class="headerlink" title="隔离：错位竞争、边缘创新"></a>隔离：错位竞争、边缘创新</h5><p>如果没有隔离，在原有大种群之中，变异很快就被稀释掉了。<br>地理隔离——生殖隔离——新物种<br>对于企业而言隔离的含义：<br>（1）内部：第一二曲线之间的隔离<br>对内：独立小团队<br>除非企业成立两个彼此独立的机构（从属于不同的价值网），来应对新的机遇，发展第二曲线。<br>（2）外部：与在位巨头的错位竞争<br>对外：错位竞争<br>与其更好，不如不同——减少竞争<br>在领先企业已经建立主导性优势的环境中，任何类似产品都会沦为鸡肋。</p><p>创业第一法则：<strong>错位竞争，与其更好，不如不同</strong>。在领先企业已经建立主导优势的环境中，任何me too的产品都会沦为鸡肋。</p><p><img src="/images/科学点亮创新：达尔进化论/1561183188428.png" alt="Alt text"></p><p>竞争错觉：通常会把在位企业的生态位看成全部可能的生存空间。<br>错位竞争的意义：第一打不过巨头、第二具有不屑于做、第三其实可能有足够大空间<br><strong>颠覆式创新往往来自于边缘</strong></p><p><img src="/images/科学点亮创新：达尔进化论/1561183667619.png" alt="Alt text"></p><h3 id="熵增定律：如何避免团队熵增，跨越发展极限点？"><a href="#熵增定律：如何避免团队熵增，跨越发展极限点？" class="headerlink" title="熵增定律：如何避免团队熵增，跨越发展极限点？"></a>熵增定律：如何避免团队熵增，跨越发展极限点？</h3><p><img src="/images/科学点亮创新：达尔进化论/1561184003034.png" alt="Alt text"><br>在经典力学思想下，世界就像一部钟表一样走动，人们可以预知未来的一切——机械论世界观</p><p>封闭系统的熵随着时间的推移不断增加，且不可逆——熵增。<br>生命系统是开放系统，具备不断从外界吸收能量，并且自我净化排出废物的能力，因此具备反熵增能力。</p><p>管理要做的只有一件事情，就是如何对抗熵增</p><blockquote><p>处理远离平衡状态下的开放系统，在与外界交换物质和能量的过程中，通过能量耗散过程和系统内部非线性动力学机制，能量达到一定程度，熵流可能为负，系统总熵变可能小于零，则系统通过熵减就形成了新的有序结构。——普利高斯</p></blockquote><p>从内部来说，凡是颠覆式的创新业务要采用独立小团队的机制。<br>因为这些创新业务经常容易被原有的业务主体所忽视和排挤。<br>对于外部来说，要谨记——与其更好，不如不同。</p><h4 id="熵增理论对公司借鉴价值"><a href="#熵增理论对公司借鉴价值" class="headerlink" title="熵增理论对公司借鉴价值"></a>熵增理论对公司借鉴价值</h4><p>世界就像一部钟表一样走动，人类可以预知走向，这就是机械论世界观，而整个工业时代和工商管理都是建立在牛顿世界观之上，但机械论存在一个巨大的bug，就是封闭系统的熵增随着时间推移不断增加，且不可逆。<br>这告诉一个结论：任何封闭系统，最终的命运是死亡。基于机械论的商业组织，商业组织也是封闭，存在熵增效应，随着时间推移，一定会变的官僚化，并最终走向死亡。所以，熵增定律解释了大公司必然走向死亡原因。</p><h4 id="华为为反熵增实践应用"><a href="#华为为反熵增实践应用" class="headerlink" title="华为为反熵增实践应用"></a>华为为反熵增实践应用</h4><p>企业要保持自己竞争力，进行反熵增管理。以反熵增为第一性原理的公司那就是华为公司。<br>2000年前，华为研发体系，事事上报，老板决策，下层行动，典型的机械论世界观指导下的组织管理，在华为公司的至暗时刻，任正非在天命之年，了解到热力学第二定律了解到熵死现象，知道华为公司需要避免的是熵死。在此之后，华为公司通过开放链接世界的动作，耗散内部能量，如财富耗散于人才，财富耗散于技术，通过避免熵死，带来华为公司新的竞争力。<br><img src="/images/科学点亮创新：达尔进化论/1561185612594.png" alt="Alt text"></p><p><img src="/images/科学点亮创新：达尔进化论/1561185952172.png" alt="Alt text"></p><h3 id="分形创新：如何提炼业务基因，孕育下一站创新？"><a href="#分形创新：如何提炼业务基因，孕育下一站创新？" class="headerlink" title="分形创新：如何提炼业务基因，孕育下一站创新？"></a>分形创新：如何提炼业务基因，孕育下一站创新？</h3><blockquote><p>解决第一曲线“怎么”变成第二曲线？<br>创新两大困境：不创新，等死；创新，找死。</p></blockquote><p>达尔文思想一个决定性的转折：尺度变换<br>生存竞争的主角不是物种，而是同一物种之内的个体；遗传变异的主角甚至不是生物个体，而是基因；基因变异的场景是个体之间的繁殖。</p><p><img src="/images/科学点亮创新：达尔进化论/1561187487970.png" alt="Alt text"><br>回到进化论，物种如何从一个物种变为另一个物种，自然界无飞跃，不是<strong>突变</strong>，而是持续迭代，分形创新。</p><p>创新可以自己生长出来，不是规划布局出来的</p><p>创新+选择=第二曲线<br>创新：第一条曲线生发出无数多样性的小S曲线<br>选择：其中某个小S曲线成长为独立的第二曲线</p><p>所谓第二曲线创新，不是放弃主营业务，布局新业务；恰恰相反，在主营业务中注入创新，新业务将是自然而然的结果。总结一下，第二曲线是在第一曲线上生长出来的，也是分形的关键。第一曲线上业务的主航道，必须要<strong>夯实主航道</strong>，随意破坏第一曲线便是自毁长城，不及时由第一曲线切换到第二曲线也可能面临死亡。</p><p><img src="/images/科学点亮创新：达尔进化论/1561188308130.png" alt="Alt text"></p><p>分形创新操作步骤<br><img src="/images/科学点亮创新：达尔进化论/1561188751326.png" alt="Alt text"></p><p><img src="/images/科学点亮创新：达尔进化论/1561188959728.png" alt="Alt text"><br><img src="/images/科学点亮创新：达尔进化论/1561189039172.png" alt="Alt text"><br><img src="/images/科学点亮创新：达尔进化论/1561189066325.png" alt="Alt text"><br><img src="/images/科学点亮创新：达尔进化论/1561189147412.png" alt="Alt text"><br><img src="/images/科学点亮创新：达尔进化论/1561189190990.png" alt="Alt text"></p><p>分形算法<br><img src="/images/科学点亮创新：达尔进化论/1561189798257.png" alt="Alt text"></p><p>分形的关键是：可迁移的、自相似的同构性<br>分形算法逆熵的核心机制是：增加了维度<br>分形算法不仅仅是一个创新哲学，也是一种过程哲学。过程哲学，当下是一，过程本身就是目的，结果反而不再重要。<br>过程比结果更重要</p><p>分形算法<br>互指迭代<br>击穿阈值点<br>同构性关系<br>夯实自己主航道</p><p>把眼前的事情做到极致，下一步自然来临</p><h3 id="涌现创新：如何塑造组织生态，让创新自然涌现？"><a href="#涌现创新：如何塑造组织生态，让创新自然涌现？" class="headerlink" title="涌现创新：如何塑造组织生态，让创新自然涌现？"></a>涌现创新：如何塑造组织生态，让创新自然涌现？</h3><p>涌现是一种特殊的分形，属于复杂性科学的概念<br>涌现——整体大于部分之和<br>随着成员数目的增加，成员之间相互作用呈指数增长，当连接度超过某一临界值引发涌现</p><p>涌现的层次性：大量个体合成了一个更高层次的新秩序</p><p>涌现公式：大量个体*自组织（简单规则+信息流+中心法则）=&gt;涌现</p><p>在复杂系统中，最重要的不是个体，而是个体之间的相互作用。<br>自组织：<strong>大量个体</strong>基于<strong>简单规则</strong>的<strong>相互作用</strong>，无需中央调控，能<strong>涌现</strong>出整体上的新秩序</p><p>创新是否可以自复制，自增长，自组织？</p><p>信息流<br>个体之间一定存在某种信息交流机制，能够将随机性变成某种趋势性<br>进化是由确定方向的正反馈促进形成的</p><p>蚂蚁自组织的两种规则——蚁群算法</p><ol><li>集中行动，通往食物源的最短路径会具有最强烈的信息素气味，沿路径行进的蚂蚁最多</li><li>随机探索，无论何时，会有一些蚂蚁随意搜索，发现新食物源</li></ol><p>中心法则<br><img src="/images/科学点亮创新：达尔进化论/1561215759525.png" alt="Alt text"></p><p><img src="/images/科学点亮创新：达尔进化论/1561215786297.png" alt="Alt text"></p><p>破界创新：用第一性原理的推理方式去发现更大尺度的第一性原理</p><p><img src="/images/科学点亮创新：达尔进化论/1561215879560.png" alt="Alt text"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;创新是发展的第一动力因，是增长的最佳引擎。面对越来越多的不确定性和非连续性，该如何培育创新物种？如何打破认知边界？如何实现持续创新？&lt;br&gt;日渐消失的增长红利（人口红利、互联网红利、全球化红利），红利消失，增长还是要靠&lt;strong&gt;创新&lt;/str
      
    
    </summary>
    
      <category term="程序人生" scheme="https://challange.github.io/categories/%E7%A8%8B%E5%BA%8F%E4%BA%BA%E7%94%9F/"/>
    
    
      <category term="程序人生" scheme="https://challange.github.io/tags/%E7%A8%8B%E5%BA%8F%E4%BA%BA%E7%94%9F/"/>
    
  </entry>
  
  <entry>
    <title>SpringMvc项目接入SpringCloud微服务的解决方案</title>
    <link href="https://challange.github.io/%E5%BE%AE%E6%9C%8D%E5%8A%A1-SpringMvc%E9%A1%B9%E7%9B%AE%E6%8E%A5%E5%85%A5SpringCloud%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/"/>
    <id>https://challange.github.io/微服务-SpringMvc项目接入SpringCloud微服务的解决方案/</id>
    <published>2019-05-26T08:05:05.000Z</published>
    <updated>2019-05-26T07:07:08.033Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>在SpringBoot项目大行其道的时代，仍有很多项目是基于SpringMvc，甚至是基于struts的，这些项目的特点是<strong>年代久远，项目庞大，设计文档存在缺漏</strong>。但是对于这些项目，我们不能放任不管，甚至很多项目还在为公司创造这价值。<br>随着公司业务的发展，原先的单体项目已经不能满足快速发展变化的业务的需求，这时候就要进行微服务改造。</p></blockquote><h3 id="方案一：Sidecar异构接入"><a href="#方案一：Sidecar异构接入" class="headerlink" title="方案一：Sidecar异构接入"></a>方案一：Sidecar异构接入</h3><p><img src="/images/SpringMvc项目接入SpringCloud微服务的解决方案/1.png" alt="Sidecar异构架构"></p><p>Sidecar项目可以看作老项目的影子项目，由Sidecar负责接入注册中心，并且发起远程调用和被远程调用。两个项目为一个整体对外提供服务。</p><h4 id="优势："><a href="#优势：" class="headerlink" title="优势："></a>优势：</h4><ol><li>sidecar可以无视语言，可以用于代理古老的java项目，也可以异构代理其他语言项目，如php、python、nodejs等</li><li>sidecar可以在不对老项目进行大规模改造的情况下快速接入SpringCloud微服务，渐进式的做微服务拆分</li></ol><h4 id="劣势"><a href="#劣势" class="headerlink" title="劣势"></a>劣势</h4><ol><li>增加项目架构和维护的复杂度，多一个项目多一个环境意味着出问题的概率又多一分</li></ol><h4 id="Sidecar项目配置"><a href="#Sidecar项目配置" class="headerlink" title="Sidecar项目配置"></a>Sidecar项目配置</h4><ol><li><p>老项目添加HealthController</p><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><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Usage</span>: 用于健康检查</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HealthController</span> </span>&#123;</span><br><span class="line">    <span class="meta">@RequestMapping</span>(<span class="string">"/openapi/health/status"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Map <span class="title">status</span><span class="params">()</span></span>&#123;</span><br><span class="line">        Map&lt;String, String&gt; map = <span class="keyword">new</span> HashMap&lt;&gt;(<span class="number">1</span>);</span><br><span class="line">        map.put(<span class="string">"status"</span>,<span class="string">"UP"</span>);</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>新建sidecar maven项目</p></li><li><p>引入依赖</p><figure class="highlight xml"><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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- sidecar 依赖--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-netflix-sidecar<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.1.0.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- eureka 注册中心--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-eureka-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.1.0.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- hystrix 熔断--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-hystrix<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.1.0.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- feign 声明式服务调用--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-openfeign<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.1.0.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p>项目启动类加入如下注解</p></li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@EnableFeignClients</span></span><br><span class="line"><span class="meta">@EnableSidecar</span></span><br></pre></td></tr></table></figure><p>不用@EnableEurekaClient，因为@EnableSidecar中包含来eureka的注册逻辑</p><ol start="5"><li>配置文件增加sidecar、eureka、feign、hystrix、ribbon配置<br>重点讲一下sidecar配置<figure class="highlight yml"><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></pre></td><td class="code"><pre><span class="line"><span class="attr">sidecar:</span></span><br><span class="line"><span class="comment"># 配置接入web的端口</span></span><br><span class="line"><span class="attr">port:</span> <span class="number">8080</span></span><br><span class="line"><span class="attr">home-page-uri:</span> <span class="attr">http://localhost:$&#123;sidecar.port&#125;/</span></span><br><span class="line"><span class="comment"># 配置接入web的健康检查rest接口，sidecar将请求该url，用以确定接入应用是否存活.</span></span><br><span class="line"><span class="attr">health-uri:</span> <span class="attr">http://localhost:$&#123;sidecar.port&#125;/openapi/health/status</span></span><br></pre></td></tr></table></figure></li></ol><h3 id="方案二：升级SpringBoot并集成SpringCloud组件"><a href="#方案二：升级SpringBoot并集成SpringCloud组件" class="headerlink" title="方案二：升级SpringBoot并集成SpringCloud组件"></a>方案二：升级SpringBoot并集成SpringCloud组件</h3><h4 id="一、升级SpringBoot-1-x版本"><a href="#一、升级SpringBoot-1-x版本" class="headerlink" title="一、升级SpringBoot 1.x版本"></a>一、升级SpringBoot 1.x版本</h4><p>在不改动Spring版本的情况下引入SrpingBoot，将项目改造为SpringBoot项目<br>SpringBoot 1.x 引用的Spring 4.x版本，而一般SpringMvc项目也是使用Spring4.x版本，如果使用Spring更老版本或未使用spring则另当别论。</p><h4 id="二、升级SpringBoot-2-x版本"><a href="#二、升级SpringBoot-2-x版本" class="headerlink" title="二、升级SpringBoot 2.x版本"></a>二、升级SpringBoot 2.x版本</h4><p>SpringBoot 2.x版本使用的是Spring5.x版本，升级2.x意味着升级spring并且需要解决spring升级带来的问题</p><h4 id="三、引入SpringCloud并集成SpringCloud组件"><a href="#三、引入SpringCloud并集成SpringCloud组件" class="headerlink" title="三、引入SpringCloud并集成SpringCloud组件"></a>三、引入SpringCloud并集成SpringCloud组件</h4><h4 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h4><h5 id="集成Eureka时，Cannot-create-Jersey-client错误"><a href="#集成Eureka时，Cannot-create-Jersey-client错误" class="headerlink" title="集成Eureka时，Cannot create Jersey client错误"></a>集成Eureka时，Cannot create Jersey client错误</h5><p>问题原因：jersey 与 fastjson 某些版本冲突导致服务无法启动<br>错误：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Caused by: com.sun.jersey.spi.inject.Errors$ErrorMessagesException: null</span><br></pre></td></tr></table></figure></p><p>解决方案：fastjson升级到 1.2.37以上版本</p><p><a href="https://github.com/alibaba/fastjson/releases/tag/1.2.37">https://github.com/alibaba/fastjson/releases/tag/1.2.37</a></p><h5 id="日期响应为linux时间戳"><a href="#日期响应为linux时间戳" class="headerlink" title="日期响应为linux时间戳"></a>日期响应为linux时间戳</h5><blockquote><p>升级SpringBoot后，原先的日期格式化失效，在不给字段加@JsonFormat注解的情况下便要思考通过全局配置来解决</p></blockquote><p><strong>正常配置</strong><br><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">spring.jackson.time-zone=GMT+8</span><br><span class="line"># 指定响应数据格式</span><br><span class="line">spring.jackson.date-format=yyyy-MM-dd HH:mm:ss</span><br><span class="line"># 指定不返回时间戳</span><br><span class="line">spring.jackson.serialization.write-dates-as-timestamps=false</span><br></pre></td></tr></table></figure></p><p>这样配置后并没有生效，继续查找原因。</p><blockquote><p>Finally, if you opt out of the Spring Boot default MVC configuration by providing your own @EnableWebMvc configuration, you can take control completely and do everything manually by using getMessageConverters from WebMvcConfigurationSupport.<br>  《Spring Boot Reference Guide》</p></blockquote><p>去掉@EnableWebMvc后，配置可以生效。</p><p>结合WebMvcAutoConfiguration理解@EnableWebMvc</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;在SpringBoot项目大行其道的时代，仍有很多项目是基于SpringMvc，甚至是基于struts的，这些项目的特点是&lt;strong&gt;年代久远，项目庞大，设计文档存在缺漏&lt;/strong&gt;。但是对于这些项目，我们不能放任不管，甚至很多项目还在为
      
    
    </summary>
    
      <category term="微服务" scheme="https://challange.github.io/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
    
      <category term="微服务" scheme="https://challange.github.io/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
  </entry>
  
  <entry>
    <title>从零开始玩转SpringCloud（三）：Feign声明式服务调用</title>
    <link href="https://challange.github.io/springcloud-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%8E%A9%E8%BD%ACSpringCloud%EF%BC%88%E4%B8%89%EF%BC%89%EF%BC%9AFeign%E5%A3%B0%E6%98%8E%E5%BC%8F%E6%9C%8D%E5%8A%A1%E8%B0%83%E7%94%A8/"/>
    <id>https://challange.github.io/springcloud-从零开始玩转SpringCloud（三）：Feign声明式服务调用/</id>
    <published>2019-05-26T04:00:30.000Z</published>
    <updated>2019-05-26T05:31:56.987Z</updated>
    
    <content type="html"><![CDATA[<h3 id="Feign-概述"><a href="#Feign-概述" class="headerlink" title="Feign 概述"></a>Feign 概述</h3><blockquote><p>在开发 Spring Cloud 微服务的时候，服务之间都是以 HTTP 接口的形式对外提供服务的，因此消费者在进行调用的时候，底层就是通过 HTTP Client 的这种方式进行访问。当然我们可以使用JDK原生的 URLConnection、Apache 的 HTTP Client、Netty 异步 Http Client，Spring 的 RestTemplate 去实现服务间的调用。但是最方便、最优雅的方式是通过 Spring Cloud Open Feign 进行服务间的调用 Spring Cloud 对 Feign 进行了增强，使 Feign 支持 Spring Mvc 的注解，并整合了 Ribbon（负载均衡）、Hystrix（熔断机制） 等，从而让 Feign 更加适合SpringCloud微服务开发。</p></blockquote><h4 id="什么是-Feign"><a href="#什么是-Feign" class="headerlink" title="什么是 Feign"></a>什么是 Feign</h4><ol><li>Feign 是一个声明式的 Web Service 客户端。它的出现使开发 Web Service 客户端变得很简单。使用 Feign 只需要创建一个接口加上对应的注解，比如：@FeignClient 注解。 Feign 有可插拔的注解，包括 Feign 注解和 AX-RS 注解。Feign 也支持编码器和解码器，Spring Cloud Open Feign 对 Feign 进行增强支持 Spring Mvc 注解，可以像 Spring Web 一样使用 HttpMessageConverters 等。</li><li>Feign 是一种声明式、模板化的 HTTP 客户端。在 Spring Cloud 中使用 Feign，可以做到使用 HTTP 请求访问远程服务，就像调用本地方法一样的，开发者完全感知不到这是在调用远程方法，更感知不到在访问 HTTP 请求。接下来介绍一下 Feign 的特性，具体如下：</li></ol><p><strong>功能</strong></p><ul><li>可插拔的注解支持，包括 Feign 注解和AX-RS注解。</li><li>支持可插拔的 HTTP 编码器和解码器。</li><li>支持 Hystrix 和它的 Fallback。</li><li>支持 Ribbon 的负载均衡。</li><li>支持 HTTP 请求和响应的压缩。Feign 是一个声明式的 WebService 客户端，它的目的就是让 Web Service 调用更加简单。它整合了 Ribbon 和 Hystrix，从而不需要开发者针对 Feign 对其进行整合。Feign 还提供了 HTTP 请求的模板，通过编写简单的接口和注解，就可以定义好 HTTP 请求的参数、格式、地址等信息。Feign 会完全代理 HTTP 的请求，在使用过程中我们只需要依赖注入 Bean，然后调用对应的方法传递参数即可。</li></ul><h3 id="Feign快速使用"><a href="#Feign快速使用" class="headerlink" title="Feign快速使用"></a>Feign快速使用</h3><ol><li><p>引入依赖</p><figure class="highlight xml"><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="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-openfeign<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p>项目启动类加入如下注解：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 开启 Feign 扫描支持 */</span></span><br><span class="line"><span class="meta">@EnableFeignClients</span></span><br></pre></td></tr></table></figure></li><li><p>Feign 接口编写</p><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><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** name指定微服务名称，path为类的基路径*/</span></span><br><span class="line"><span class="meta">@FeignClient</span>(name = <span class="string">"user"</span>, path = <span class="string">"/sys/user"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserFeignSerivce</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 插入user</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> record</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping</span>(<span class="string">"/insertUser"</span>)</span><br><span class="line">    <span class="function">BaseResult <span class="title">insertUser</span><span class="params">(User record)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><p>}</p><ol start="4"><li>Feign 接口引用</li></ol><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Autowired</span></span><br><span class="line"><span class="keyword">private</span> UserFeignSerivce userFeignSerivce;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> BaseResult <span class="title">insert</span><span class="params">(User record)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> userFeignSerivce.insertUser(record);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Feign-工作原理"><a href="#Feign-工作原理" class="headerlink" title="Feign 工作原理"></a>Feign 工作原理</h3><ul><li>在开发微服务应用时，我们会在主程序入口添加 @EnableFeignClients 注解开启对 Feign Client 扫描加载处理。根据 Feign Client 的开发规范，定义接口并加 @FeignClients 注解。</li><li>当程序启动时，会进行包扫描，扫描所有 @FeignClients 的注解的类，并将这些信息注入 Spring IOC 容器中。当定义的 Feign 接口中的方法被调用时，通过JDK的代理的方式，来生成具体的 RequestTemplate。当生成代理时，Feign 会为每个接口方法创建一个 RequetTemplate 对象，该对象封装了 HTTP 请求需要的全部信息，如请求参数名、请求方法等信息都是在这个过程中确定的。</li><li>然后由 RequestTemplate 生成 Request，然后把 Request 交给 Client 去处理，这里指的 Client 可以是 JDK 原生的 URLConnection、Apache 的 Http Client 也可以是 Okhttp。最后 Client 被封装到 LoadBalanceclient 类，这个类结合 Ribbon 负载均衡发起服务之间的调用。</li></ul><h4 id="FeignClient-注解"><a href="#FeignClient-注解" class="headerlink" title="@FeignClient 注解"></a>@FeignClient 注解</h4><ul><li>name：指定 Feign Client 的名称，如果项目使用了 Ribbon，name 属性会作为微服务的名称，用于服务发现。</li><li>url：url 一般用于调试，可以手动指定 @FeignClient 调用的地址。</li><li>decode404：当发生404错误时，如果该字段为 true，会调用 decoder 进行解码，否则抛出 FeignException。</li><li>configuration：Feign 配置类，可以自定义 Feign 的 Encoder、Decoder、LogLevel、Contract。</li><li>fallback：定义容错的处理类，当调用远程接口失败或超时时，会调用对应接口的容错逻辑，fallback 指定的类必须实现 @FeignClient 标记的接口。</li><li>fallbackFactory：工厂类，用于生成 fallback 类示例，通过这个属性我们可以实现每个接口通用的容错逻辑，减少重复的代码。</li><li>path：定义当前 FeignClient 的统一前缀。</li></ul><h3 id="Feign-使用注意事项"><a href="#Feign-使用注意事项" class="headerlink" title="Feign 使用注意事项"></a>Feign 使用注意事项</h3><ol><li>使用Spring Cloud Feign，如果接口参数中带有@PathVariable路径参数,则要用value=””标明对应的参数,否则会抛出 <strong>java.lang.IllegalStateException: PathVariable annotation was empty on param 0.</strong> 异常。如：@PathVariable(value = “groupType”) String groupType</li><li>使用Spring Cloud Feign，如果接口参数中带有@RequestParam参数，@RequestParam 不能省略</li><li>在使用SpringBoot2.1.0以后版本时，多个接口的@FeignClient的name相同，场景调用同一微服务项目，但是不同模块(Controller)，可能会抛出 <strong>xx.FeignClientSpecification’, defined in null, could not be registered</strong> 异常，解决方案：增加配置spring.main.allow-bean-definition-overriding=true，但会导致@FeignClient的配置覆盖，或者<a href="http://cloud.spring.io/spring-cloud-static/Edgware.SR2/single/spring-cloud.html#_creating_feign_clients_manually">手动配置FeignClient</a>。<br>参考文章：<a href="https://blog.csdn.net/u012211603/article/details/84312709">https://blog.csdn.net/u012211603/article/details/84312709</a></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;Feign-概述&quot;&gt;&lt;a href=&quot;#Feign-概述&quot; class=&quot;headerlink&quot; title=&quot;Feign 概述&quot;&gt;&lt;/a&gt;Feign 概述&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;在开发 Spring Cloud 微服务的时候，服务之间都是以 H
      
    
    </summary>
    
      <category term="SpringCloud" scheme="https://challange.github.io/categories/SpringCloud/"/>
    
    
      <category term="SpringCloud" scheme="https://challange.github.io/tags/SpringCloud/"/>
    
      <category term="Feign" scheme="https://challange.github.io/tags/Feign/"/>
    
  </entry>
  
  <entry>
    <title>Java基础学习——泛型</title>
    <link href="https://challange.github.io/DIY-DIY%E2%80%94%E2%80%94%E6%B3%9B%E5%9E%8B/"/>
    <id>https://challange.github.io/DIY-DIY——泛型/</id>
    <published>2019-05-06T08:05:05.000Z</published>
    <updated>2019-05-26T04:04:06.164Z</updated>
    
    <content type="html"><![CDATA[<h3 id="范型的用法"><a href="#范型的用法" class="headerlink" title="范型的用法"></a>范型的用法</h3><ol><li>类范型，类名后面尖括号指定T或者其他名称，T只是一个代号，实例化时，指定T的具体类型</li><li>方法范型，方法名前面用尖括号指定T或者其他名称，方法参数需能推断出T的类型，调用方法时获得范型具体类型</li><li>尖括号中的范型T或其他名称只是一个指代，具体类型由实例化或者方法调用时得知</li><li>对于static的变量不能用范型</li></ol><h3 id="1-Java如果没有泛型会有什么灾难？"><a href="#1-Java如果没有泛型会有什么灾难？" class="headerlink" title="1. Java如果没有泛型会有什么灾难？"></a>1. Java如果没有泛型会有什么灾难？</h3><p>泛型是参数化类型，在使用时告诉编辑器使用什么类型。</p><ol><li><p>程序可控性：泛型有限定类型，泛型可以使程序执行时的类型是确定的，避免了程序的不可控性。在泛型引入之前，对于不确定类型需要使用Object，然后类型强转，此处带来的问题，Object含义不清，使代码不易懂，同时类型无界限限定，很可能会想当然使用错误类型。泛型的引入，在开发是对类或者方法的范围做一个限定，使类和方法运行在有限的类型中，<strong>使程序更清晰，可控性更强</strong><br><img src="/images/DIY——泛型/1.png" alt="Alt text"></p><blockquote><p>上图是ArrayList的add操作，jdk1.5前参数为Object，也就意味着任何类型都可以放入list中。 </p></blockquote></li><li><p>提升复用性：解耦类或方法对类型之间的约束，通过参数化类型，使执行相同功能的方法、类能够复用代码，如果没有泛型，相同的代码每个类都要写一份，带来了大量冗余代码。一个场景，CRUD在web项目广泛使用，逻辑类似，每个类型写一遍crud方法就会造成很多冗余代码，这时可以封装一个泛型基类，一份代码给所有类型用。</p></li><li>使用泛型，编译时就能确保容器中插入对象的类型安全，编译器发现问题要好于运行时发现问题，运行时出问题很有可能就意味着生产事故了。我们应该避免产生<strong>隐藏较深</strong>的bug，让bug越早暴露越好。<h3 id="2-List-lt-extends-T-gt-和List-lt-super-T-gt-有哪些区别？"><a href="#2-List-lt-extends-T-gt-和List-lt-super-T-gt-有哪些区别？" class="headerlink" title="2. List&lt;? extends T&gt;和List&lt;? super T&gt;有哪些区别？"></a>2. List&lt;? extends T&gt;和List&lt;? super T&gt;有哪些区别？</h3></li><li>List&lt;? extends T&gt;，Get Frist适用于消费集合为主的场景，List&lt;? extends T&gt;可以赋值给任何T及T子类的场景，上界为T，put受限，add只能放入null，不能放入其他元素;使用List&lt;? extends T&gt;相当于一个视图，不具有修改能力，但被赋值的原集合修改，视图可以感知。</li><li>List&lt;? super T&gt;，Put Frist适用于生产集合为主的场景，List&lt;? super T&gt;可以赋值给任何T及T的父类集合，下界为T，put可以放入T及T的父类类型，get受限，get可以使用，但是类型会被擦除到Object。</li><li>不同点维度:赋值，put、get</li><li>List&lt;? extends T&gt;类比于数据库的视图，T决定了结果集列的属性，相同与视图的List&lt;? extends T&gt;也是主要用于展示，如果要添加或者删除记录，则应修改原先的list。</li></ol><h3 id="3-类名-lt-super-T-gt-存在哪些实际应用场景？（参考Comparator）"><a href="#3-类名-lt-super-T-gt-存在哪些实际应用场景？（参考Comparator）" class="headerlink" title="3. 类名&lt;? super T&gt;存在哪些实际应用场景？（参考Comparator）"></a>3. 类名&lt;? super T&gt;存在哪些实际应用场景？（参考Comparator）</h3><p>Comparator&lt;? super T&gt; 准许为所有的子类使用相同的比较器。通常我们会使用Comparator自己定义对象的比较规则，如果要比较猫的重量，那么就实现猫重量的比较器，比较狗的重量，则需要实现狗重量的比较器，但如果要允许将猫与狗的重量进行比较，则就需要Comparator&lt;? super T&gt;来发挥作用，定义animal的比较器，就可以比较猫和狗的重量。&lt;? super T&gt;使的方法可以使方法不仅可以作用于父类也可以作用于子类，增加了代码的复用性。</p><h3 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h3><p>泛型会擦除传入实体的属性，如果使用&lt; T &gt;，则T的类型在代码执行时会被擦除，因为Object是所有类的父类，所以此时T只具有Object的属性，但是当使用&lt; ? super T&gt;或&lt;? extends T&gt; ,定义边界，T的类型会被保留到边界T。</p><p>泛型在Java1.5引入，在泛型引入前java语言已经有了广泛的发展，较好的生态，拥有大量的类库，因此java在引入泛型的同时，也考虑到了兼容过去代码（java升级良好的兼容性我想是java能够经久不衰的重要原因）。java的泛型是具有类型擦除特性的<br>泛型具有类型推断能力，但是类型推断仅在赋值时有效。如果将泛型方法返回结果作为参数，这时并不会执行类型推断。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;范型的用法&quot;&gt;&lt;a href=&quot;#范型的用法&quot; class=&quot;headerlink&quot; title=&quot;范型的用法&quot;&gt;&lt;/a&gt;范型的用法&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;类范型，类名后面尖括号指定T或者其他名称，T只是一个代号，实例化时，指定T的具体类型&lt;/li&gt;
&lt;li&gt;
      
    
    </summary>
    
      <category term="Java基础" scheme="https://challange.github.io/categories/Java%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="DIY" scheme="https://challange.github.io/tags/DIY/"/>
    
      <category term="Java基础" scheme="https://challange.github.io/tags/Java%E5%9F%BA%E7%A1%80/"/>
    
      <category term="泛型" scheme="https://challange.github.io/tags/%E6%B3%9B%E5%9E%8B/"/>
    
  </entry>
  
  <entry>
    <title>Git操作整理</title>
    <link href="https://challange.github.io/Git-GIt%E6%93%8D%E4%BD%9C%E6%95%B4%E7%90%86/"/>
    <id>https://challange.github.io/Git-GIt操作整理/</id>
    <published>2019-05-06T08:05:05.000Z</published>
    <updated>2019-05-08T16:24:18.164Z</updated>
    
    <content type="html"><![CDATA[<h3 id="基础操作"><a href="#基础操作" class="headerlink" title="基础操作"></a>基础操作</h3><h4 id="查看全局配置信息"><a href="#查看全局配置信息" class="headerlink" title="查看全局配置信息"></a>查看全局配置信息</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --list</span><br></pre></td></tr></table></figure><h4 id="设置全局配置信息"><a href="#设置全局配置信息" class="headerlink" title="设置全局配置信息"></a>设置全局配置信息</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global user.name &quot;wwyz&quot;</span><br></pre></td></tr></table></figure><h4 id="初始化本地仓库"><a href="#初始化本地仓库" class="headerlink" title="初始化本地仓库"></a>初始化本地仓库</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git init</span><br></pre></td></tr></table></figure><h4 id="将文件加入git版本库"><a href="#将文件加入git版本库" class="headerlink" title="将文件加入git版本库"></a>将文件加入git版本库</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add &quot;文件名&quot;</span><br></pre></td></tr></table></figure><h4 id="拉取代码"><a href="#拉取代码" class="headerlink" title="拉取代码"></a>拉取代码</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone &quot;版本库地址&quot; &quot;目录名&quot;</span><br></pre></td></tr></table></figure><h4 id="查看文件状态"><a href="#查看文件状态" class="headerlink" title="查看文件状态"></a>查看文件状态</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git status</span><br></pre></td></tr></table></figure><h4 id="提交代码"><a href="#提交代码" class="headerlink" title="提交代码"></a>提交代码</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git commit &quot;说明&quot;</span><br><span class="line">git commit --amend # 重新提交</span><br></pre></td></tr></table></figure><h4 id="移除文件-移除后文件不在被版本控制"><a href="#移除文件-移除后文件不在被版本控制" class="headerlink" title="移除文件,移除后文件不在被版本控制"></a>移除文件,移除后文件不在被版本控制</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git rm</span><br><span class="line">git rm --cached readme.txt #缓存中移除</span><br></pre></td></tr></table></figure><h4 id="移动文件"><a href="#移动文件" class="headerlink" title="移动文件"></a>移动文件</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git mv &quot;文件路径&quot; &quot;目标文件路径&quot;</span><br></pre></td></tr></table></figure><h4 id="查看提交文件历史"><a href="#查看提交文件历史" class="headerlink" title="查看提交文件历史"></a>查看提交文件历史</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git log</span><br><span class="line">git log -p -2 #-p选项展开显示每次提交的内容差异，用-2则仅显示最近的两次更新</span><br></pre></td></tr></table></figure><h4 id="查看远程仓库"><a href="#查看远程仓库" class="headerlink" title="查看远程仓库"></a>查看远程仓库</h4><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">git remote -v</span><br><span class="line"># 添加远程仓库</span><br><span class="line">git remote add pb git://github.com/paulboone/ticgit.git</span><br><span class="line"># 拉取远程代码更新</span><br><span class="line">git fetch pb</span><br><span class="line"># 查看远程仓库的信息</span><br><span class="line">git remote show origin</span><br></pre></td></tr></table></figure><h4 id="推送提交的代码"><a href="#推送提交的代码" class="headerlink" title="推送提交的代码"></a>推送提交的代码</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin master</span><br></pre></td></tr></table></figure><h4 id="忽略文件"><a href="#忽略文件" class="headerlink" title="忽略文件"></a>忽略文件</h4><blockquote><p>通过在项目根目录配置.gitignore文件忽略要忽略的文件/文件夹。<br>Java项目一般忽略：.idea、target、out、classes<br>前端项目一般忽略：.idea、node_modules、dist<br> .gitignore 格式规范如下：</p><ol><li>所有空行或者以注释符号 ＃ 开头的行都会被 Git 忽略。</li><li>可以使用标准的 glob(shell 所使用的简化了的正则表达式)模式匹配。</li><li>匹配模式最后跟反斜杠（/）说明要忽略的是目录。</li><li>要忽略指定模式以外的文件或目录，可以在模式前加上惊叹号（!）取反。</li></ol></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">*.[oa]  # 忽略所有以 .o 或 .a 结尾的文件</span><br><span class="line">*~      # 忽略所有以波浪符（~）结尾的文件</span><br></pre></td></tr></table></figure><h4 id="Tag打标签"><a href="#Tag打标签" class="headerlink" title="Tag打标签"></a>Tag打标签</h4><blockquote><p>对某一时间点上的版本打上标签，通常结合版本使用。Git 使用的标签有两种类型：轻量级的（lightweight）和含附注的（annotated）。轻量级标签就像是个不会变化的分支，实际上它就是个指向特定提交对象的引用。而含附注标签，实际上是存储在仓库中的一个独立对象，它有自身的校验和信息，包含着标签的名字，电子邮件地址和日期，以及标签说明，标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。一般我们都建议使用含附注型的标签，以便保留相关信息；当然，如果只是临时性加注标签，或者不需要旁注额外信息，用轻量级标签也没问题。</p></blockquote><h5 id="列出已有标签"><a href="#列出已有标签" class="headerlink" title="列出已有标签"></a>列出已有标签</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag</span><br></pre></td></tr></table></figure><h3 id="分支"><a href="#分支" class="headerlink" title="分支"></a>分支</h3><blockquote><p>使用分支意味着你可以从开发主线上分离开来，然后在不影响主线的同时继续工作。在很多版本控制系统中，这是个昂贵的过程，常常需要创建一个源代码目录的完整副本，对大型项目来说会花费很长时间。有人把 Git 的分支模型称为“必杀技特性”，而正是因为它，将 Git 从版本控制系统家族里区分出来。Git 有何特别之处呢？Git 的分支可谓是难以置信的轻量级，它的新建操作几乎可以在瞬间完成，并且在不同分支间切换起来也差不多一样快。</p></blockquote><h4 id="什么是分支"><a href="#什么是分支" class="headerlink" title="什么是分支"></a>什么是分支</h4><blockquote><p>在 Git 中提交时，会保存一个提交（commit）对象，该对象包含一个指向暂存内容快照的指针，包含本次提交的作者等相关附属信息，包含零个或多个指向该提交对象的父对象指针：首次提交是没有直接祖先的，普通提交有一个祖先，由两个或多个分支合并产生的提交则有多个祖先。</p></blockquote><h4 id="新建分支"><a href="#新建分支" class="headerlink" title="新建分支"></a>新建分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch &quot;分支名&quot;</span><br></pre></td></tr></table></figure><h4 id="切换分支"><a href="#切换分支" class="headerlink" title="切换分支"></a>切换分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout &quot;分支名&quot;</span><br></pre></td></tr></table></figure><h4 id="合并分支"><a href="#合并分支" class="headerlink" title="合并分支"></a>合并分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># 将其他分支合并到本分支</span><br><span class="line">git merge &quot;分支名&quot;</span><br></pre></td></tr></table></figure><h4 id="分支变基"><a href="#分支变基" class="headerlink" title="分支变基"></a>分支变基</h4><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">git rebase</span><br><span class="line">#  将多次提交合并为一次提交，-i表示弹出交互式的界面让用户编辑完成合并操作</span><br><span class="line">git rebase -i  &quot;开始提交点&quot; &quot;结束提交点&quot;</span><br><span class="line"># 将某一段commit粘贴到另一个分支上</span><br><span class="line">git rebase &quot;开始提交点&quot; &quot;结束提交点&quot;  --onto &quot;分支名称&quot;</span><br></pre></td></tr></table></figure><hr><h4 id="Git官方文档"><a href="#Git官方文档" class="headerlink" title="Git官方文档"></a><a href="https://git-scm.com/book/zh/v2">Git官方文档</a></h4><h4 id="Git教程-廖雪峰"><a href="#Git教程-廖雪峰" class="headerlink" title="Git教程-廖雪峰"></a><a href="https://www.liaoxuefeng.com/wiki/896043488029600">Git教程-廖雪峰</a></h4>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;基础操作&quot;&gt;&lt;a href=&quot;#基础操作&quot; class=&quot;headerlink&quot; title=&quot;基础操作&quot;&gt;&lt;/a&gt;基础操作&lt;/h3&gt;&lt;h4 id=&quot;查看全局配置信息&quot;&gt;&lt;a href=&quot;#查看全局配置信息&quot; class=&quot;headerlink&quot; title=&quot;查
      
    
    </summary>
    
      <category term="Git" scheme="https://challange.github.io/categories/Git/"/>
    
    
      <category term="Git" scheme="https://challange.github.io/tags/Git/"/>
    
  </entry>
  
  <entry>
    <title>云原生时代下的12-Factor应用原则</title>
    <link href="https://challange.github.io/%E5%BE%AE%E6%9C%8D%E5%8A%A1-%E4%BA%91%E5%8E%9F%E7%94%9F%E6%97%B6%E4%BB%A3%E4%B8%8B%E7%9A%8412-Factor%E5%BA%94%E7%94%A8%E5%8E%9F%E5%88%99/"/>
    <id>https://challange.github.io/微服务-云原生时代下的12-Factor应用原则/</id>
    <published>2019-05-06T08:05:05.000Z</published>
    <updated>2019-05-08T16:30:42.471Z</updated>
    
    <content type="html"><![CDATA[<blockquote><ul><li>使用标准化流程自动配置，从而使新的开发者花费<strong>最少的学习成本</strong>加入这个项目。</li><li>和操作系统之间尽可能的划清界限，在各个系统中提供最大的<strong>可移植性</strong>。</li><li>适合部署在现代的云计算平台，从而在服务器和系统管理方面<strong>节省资源</strong>。</li><li>将开发环境和生产环境的差异降至最低，并使用<strong>持续交付</strong>实施敏捷开发。</li><li><strong>易拓展</strong>：可以在工具、架构和开发流程不发生明显变化的前提下实现扩展。</li></ul></blockquote><h3 id="I-基准代码——一份基准代码（Codebase），多份部署（deploy）"><a href="#I-基准代码——一份基准代码（Codebase），多份部署（deploy）" class="headerlink" title="I. 基准代码——一份基准代码（Codebase），多份部署（deploy）"></a>I. 基准代码——一份基准代码（Codebase），多份部署（deploy）</h3><p><strong>12-Factor应用</strong>通常会使用版本控制系统加以管理，如Git, Mercurial, Subversion。一份用来跟踪代码所有修订版本的数据库被称作 <strong>代码库</strong>（code repository, code repo, repo）。在类似 SVN 这样的集中式版本控制系统中，<strong>基准代码</strong> 就是指控制系统中的这一份代码库；而在 Git 那样的分布式版本控制系统中，<strong>基准代码</strong> 则是指最上游的那份代码库。</p><p>一份代码库对应多份部署</p><p>基准代码和应用之间总是保持一一对应的关系：<br><img src="/images/云原生时代下的12-Factor应用原则/1.png" alt="Alt text"></p><p>一旦有多个基准代码，就不能称为一个应用，而是一个分布式系统。分布式系统中的每一个组件都是一个应用，每一个应用可以分别使用 12-Factor 进行开发。<br>多个应用共享一份基准代码是有悖于 12-Factor 原则的。解决方案是将共享的代码<strong>拆分</strong>为独立的类库，然后使用 <strong>依赖管理</strong> 策略去加载它们（<strong>然而多个应用共享一份基准代码缺在很多公司很常见，特别在Two B的公司，很多公司会有定制需求</strong>）。<br>尽管每个应用只对应一份基准代码，但可以同时存在多份部署。每份 <strong>部署</strong> 相当于运行了一个应用的实例。通常会有一个生产环境，一个或多个预发布环境。此外，每个开发人员都会在自己本地环境运行一个应用实例，这些都相当于一份部署。·</p><p>所有部署的基准代码相同，但每份部署可以使用其不同的版本。比如，开发人员可能有一些提交还没有同步至预发布环境；预发布环境也有一些提交没有同步至生产环境。但它们都共享一份基准代码，我们就认为它们只是相同应用的不同部署而已。</p><h3 id="II-依赖——显式声明依赖关系（-dependency-）"><a href="#II-依赖——显式声明依赖关系（-dependency-）" class="headerlink" title="II. 依赖——显式声明依赖关系（ dependency ）"></a>II. 依赖——显式声明依赖关系（ dependency ）</h3><p>大多数编程语言都会提供一个打包系统，用来为各个类库提供打包服务，就像 Perl 的 CPAN 或是 Ruby 的 Rubygems 。通过打包系统安装的类库可以是系统级的（称之为 “site packages”），或仅供某个应用程序使用，部署在相应的目录中（称之为 “vendoring” 或 “bunding”）。</p><p>12-Factor规则下的应用程序<strong>不会隐式依赖系统级的类库</strong>。 它一定通过 <strong>依赖清单</strong> ，确切地声明所有依赖项。此外，在运行过程中通过 依赖隔离 工具来确保程序不会调用系统中存在但清单中未声明的依赖项。这一做法会统一应用到生产和开发环境。</p><p>例如， Ruby 的 Bundler 使用 Gemfile 作为依赖项声明清单，使用 bundle exec 来进行依赖隔离。Python 中则可分别使用两种工具 – Pip 用作依赖声明， Virtualenv 用作依赖隔离。甚至 C 语言也有类似工具， Autoconf 用作依赖声明，静态链接库用作依赖隔离。无论用什么工具，依赖声明和依赖隔离必须一起使用，否则无法满足 12-Factor 规范。</p><p>显式声明依赖的优点之一是为新进开发者简化了环境配置流程。新进开发者可以检出应用程序的基准代码，安装编程语言环境和它对应的依赖管理工具，只需通过一个 构建命令 来安装所有的依赖项，即可开始工作。例如，Ruby/Bundler 下使用 bundle install，而 Clojure/Leiningen 则是 lein deps。</p><p>12-Factor 应用同样不会隐式依赖某些系统工具，如 ImageMagick 或是curl。即使这些工具存在于几乎所有系统，但终究无法保证所有未来的系统都能支持应用顺利运行，或是能够和应用兼容。如果应用必须使用到某些系统工具，那么这些工具应该被包含在应用之中。</p><h3 id="III-配置——在环境中存储配置"><a href="#III-配置——在环境中存储配置" class="headerlink" title="III. 配置——在环境中存储配置"></a>III. 配置——在环境中存储配置</h3><p>通常，应用的 <strong>配置</strong> 在不同 <strong>部署</strong> (预发布、生产环境、开发环境等等)间会有很大差异。这其中包括：</p><ul><li>数据库，Memcached，以及其他 后端服务 的配置</li><li>第三方服务的证书，如 Amazon S3、Twitter 等</li><li>每份部署特有的配置，如域名等</li></ul><p>有些应用在代码中使用常量保存配置，这与 12-Factor 所要求的代码和配置严格分离显然大相径庭。配置文件在各部署间存在大幅差异，代码却完全一致。</p><p>判断一个应用是否正确地将配置排除在代码之外，一个简单的方法是看该应用的基准代码是否可以立刻开源，而不用担心会暴露任何敏感的信息。</p><p>需要指出的是，这里定义的“配置”并不包括应用的内部配置，比如 Rails 的 config/routes.rb，或是使用 Spring 时 代码模块间的依赖注入关系 。这类配置在不同部署间不存在差异，所以应该写入代码。</p><p>另外一个解决方法是使用配置文件，但不把它们纳入版本控制系统，就像 Rails 的 config/database.yml 。这相对于在代码中使用常量已经是长足进步，但仍然有缺点：总是会不小心将配置文件签入了代码库；配置文件的可能会分散在不同的目录，并有着不同的格式，这让找出一个地方来统一管理所有配置变的不太现实。更糟的是，这些格式通常是语言或框架特定的。</p><p>12-Factor推荐将应用的配置存储于 <strong>环境变量</strong> 中（ env vars, env ）。环境变量可以非常方便地在不同的部署间做修改，却不动一行代码；与配置文件不同，不小心把它们签入代码库的概率微乎其微；与一些传统的解决配置问题的机制（比如 Java 的属性配置文件）相比，环境变量与语言和系统无关。</p><p>配置管理的另一个方面是分组。有时应用会将配置按照特定部署进行分组（或叫做“环境”），例如Rails中的 development,test, 和 production 环境。这种方法无法轻易扩展：更多部署意味着更多新的环境，例如 staging 或 qa 。 随着项目的不断深入，开发人员可能还会添加他们自己的环境，比如 joes-staging ，这将导致各种配置组合的激增，从而给管理部署增加了很多不确定因素。</p><p>12-Factor 应用中，环境变量的粒度要足够小，且相对独立。它们永远也不会组合成一个所谓的“环境”，而是独立存在于每个部署之中。当应用程序不断扩展，需要更多种类的部署时，这种配置管理方式能够做到平滑过渡。</p><h3 id="IV-后端服务——把后端服务当作附加资源-backing-services"><a href="#IV-后端服务——把后端服务当作附加资源-backing-services" class="headerlink" title="IV. 后端服务——把后端服务当作附加资源(backing services)"></a>IV. 后端服务——把后端服务当作附加资源(backing services)</h3><p>后端服务是指程序运行所需要的通过网络调用的各种服务，如数据库（MySQL，CouchDB），消息/队列系统（RabbitMQ，Beanstalkd），SMTP 邮件发送服务（Postfix），以及缓存系统（Memcached）。</p><p>类似数据库的后端服务，通常由部署应用程序的系统管理员一起管理。除了本地服务之外，应用程序有可能使用了第三方发布和管理的服务。示例包括 SMTP（例如 Postmark），数据收集服务（例如 New Relic 或 Loggly），数据存储服务（如 Amazon S3），以及使用 API 访问的服务（例如 Twitter, Google Maps, Last.fm）。</p><p>12-Factor 应用不会区别对待本地或第三方服务。 对应用程序而言，两种都是附加资源，通过一个 url 或是其他存储在 <strong>配置</strong> 中的服务定位/服务证书来获取数据。12-Factor 应用的任意 部署 ，都应该可以在不进行任何代码改动的情况下，将本地 MySQL 数据库换成第三方服务（例如 Amazon RDS）。类似的，本地 SMTP 服务应该也可以和第三方 SMTP 服务（例如 Postmark ）互换。上述 2 个例子中，仅需修改配置中的资源地址。</p><p>每个不同的后端服务是一份 <strong>资源</strong> 。例如，一个 MySQL 数据库是一个资源，两个 MySQL 数据库（用来数据分区）就被当作是 2 个不同的资源。12-Factor 应用将这些数据库都视作 附加资源 ，这些资源和它们附属的部署保持松耦合。</p><p>一种部署附加4个后端服务<br><img src="/images/云原生时代下的12-Factor应用原则/2.png" alt="Alt text"></p><p>部署可以按需加载或卸载资源。例如，如果应用的数据库服务由于硬件问题出现异常，管理员可以从最近的备份中恢复一个数据库，卸载当前的数据库，然后加载新的数据库 – 整个过程都不需要修改代码。</p><h3 id="V-构建，发布，运行——严格分离构建和运行"><a href="#V-构建，发布，运行——严格分离构建和运行" class="headerlink" title="V. 构建，发布，运行——严格分离构建和运行"></a>V. 构建，发布，运行——严格分离构建和运行</h3><p>基准代码 转化为一份部署(非开发环境)需要以下三个阶段：</p><ul><li><strong>构建阶段</strong> 是指将代码仓库转化为可执行包的过程。构建时会使用指定版本的代码，获取和打包 依赖项，编译成二进制文件和资源文件。</li><li><strong>发布阶段</strong> 会将构建的结果和当前部署所需 配置 相结合，并能够立刻在运行环境中投入使用。</li><li><strong>运行阶段</strong> （或者说“运行时”）是指针对选定的发布版本，在执行环境中启动一系列应用程序 进程。<br>代码被构建，然后和配置结合成为发布版本</li></ul><p>12-factor 应用严格区分构建，发布，运行这三个步骤。 举例来说，直接修改处于运行状态的代码是非常不可取的做法，因为这些修改很难再同步回构建步骤。</p><p>部署工具通常都提供了发布管理工具，最引人注目的功能是退回至较旧的发布版本。比如， Capistrano 将所有发布版本都存储在一个叫 releases 的子目录中，当前的在线版本只需映射至对应的目录即可。该工具的 rollback 命令可以很容易地实现回退版本的功能。</p><p><img src="/images/云原生时代下的12-Factor应用原则/3.png" alt="Alt text"></p><p>每一个发布版本必须对应一个唯一的发布 ID，例如可以使用发布时的时间戳（2011-04-06-20:32:17），亦或是一个增长的数字（v100）。发布的版本就像一本只能追加的账本，一旦发布就不可修改，任何的变动都应该产生一个新的发布版本。</p><p>新的代码在部署之前，需要开发人员触发构建操作。但是，运行阶段不一定需要人为触发，而是可以自动进行。如服务器重启，或是进程管理器重启了一个崩溃的进程。因此，运行阶段应该保持尽可能少的模块，这样假设半夜发生系统故障而开发人员又捉襟见肘也不会引起太大问题。构建阶段是可以相对复杂一些的，因为错误信息能够立刻展示在开发人员面前，从而得到妥善处理。</p><h3 id="VI-进程——以一个或多个无状态进程运行应用"><a href="#VI-进程——以一个或多个无状态进程运行应用" class="headerlink" title="VI. 进程——以一个或多个无状态进程运行应用"></a>VI. 进程——以一个或多个无状态进程运行应用</h3><p>运行环境中，应用程序通常是以一个和多个 进程 运行的。</p><p>最简单的场景中，代码是一个独立的脚本，运行环境是开发人员自己的笔记本电脑，进程由一条命令行（例如python my_script.py）。另外一个极端情况是，复杂的应用可能会使用很多 进程类型 ，也就是零个或多个进程实例。</p><p>12-Factor 应用的进程<strong>必须无状态且无共享</strong> 。 任何需要持久化的数据都要存储在 <strong>后端服务</strong> 内，比如数据库。</p><p>内存区域或磁盘空间可以作为进程在做某种事务型操作时的<strong>缓存</strong>，例如下载一个很大的文件，对其操作并将结果写入数据库的过程。12-Factor应用根本不用考虑这些缓存的内容是不是可以保留给之后的请求来使用，这是因为应用启动了多种类型的进程，将来的请求多半会由其他进程来服务。即使在只有一个进程的情形下，先前保存的数据（内存或文件系统中）也会因为重启（如代码部署、配置更改、或运行环境将进程调度至另一个物理区域执行）而丢失。</p><p>源文件打包工具（Jammit, django-compressor） 使用文件系统来缓存编译过的源文件。12-Factor 应用更倾向于在 构建步骤 做此动作——正如 Rails资源管道 ，而不是在运行阶段。</p><p>一些互联网系统依赖于 “粘性 session”， 这是指将用户 session 中的数据缓存至某进程的内存中，并将同一用户的后续请求路由到同一个进程。粘性 session 是 12-Factor 极力反对的。Session 中的数据应该保存在诸如 Memcached 或 Redis 这样的带有过期时间的缓存中。</p><h3 id="VII-端口绑定——通过端口绑定提供服务"><a href="#VII-端口绑定——通过端口绑定提供服务" class="headerlink" title="VII. 端口绑定——通过端口绑定提供服务"></a>VII. 端口绑定——通过端口绑定提供服务</h3><p>互联网应用有时会运行于服务器的容器之中。例如 PHP 经常作为 Apache HTTPD 的一个模块来运行，正如 Java 运行于 Tomcat 。</p><p>12-Factor 应用完全自我加载 而不依赖于任何网络服务器就可以创建一个面向网络的服务。互联网应用 通过端口绑定来提供服务 ，并监听发送至该端口的请求。</p><p>本地环境中，开发人员通过类似 <a href="http://localhost:5000/">http://localhost:5000/</a> 的地址来访问服务。在线上环境中，请求统一发送至公共域名而后路由至绑定了端口的网络进程。</p><p>通常的实现思路是，<strong>将网络服务器类库通过 依赖声明 载入应用</strong>。例如，Python 的 Tornado, Ruby 的Thin , Java 以及其他基于 JVM 语言的 Jetty。完全由 用户端 ，确切的说应该是应用的代码，发起请求。和运行环境约定好绑定的端口即可处理这些请求。</p><p>HTTP 并不是唯一一个可以由端口绑定提供的服务。其实几乎所有服务器软件都可以通过进程绑定端口来等待请求。例如，使用 XMPP 的 ejabberd ， 以及使用 Redis 协议 的 Redis 。</p><p>还要指出的是，端口绑定这种方式也意味着一个应用可以成为另外一个应用的 后端服务 ，调用方将服务方提供的相应 URL 当作资源存入 配置 以备将来调用。</p><h3 id="VIII-并发——通过进程模型进行扩展"><a href="#VIII-并发——通过进程模型进行扩展" class="headerlink" title="VIII. 并发——通过进程模型进行扩展"></a>VIII. 并发——通过进程模型进行扩展</h3><p>任何计算机程序，一旦启动，就会生成一个或多个进程。互联网应用采用多种进程运行方式。例如，PHP 进程作为 Apache 的子进程存在，随请求按需启动。Java 进程则采取了相反的方式，在程序启动之初 JVM 就提供了一个超级进程储备了大量的系统资源(CPU 和内存)，并通过多线程实现内部的并发管理。上述 2 个例子中，进程是开发人员可以操作的最小单位。</p><p>扩展表现为运行中的进程，工作多样性表现为进程类型。<br><img src="/images/云原生时代下的12-Factor应用原则/4.png" alt="Alt text"></p><p>在 12-factor 应用中，<strong>进程是一等公民</strong>。12-Factor 应用的进程主要借鉴于 <strong>unix 守护进程模型</strong> 。开发人员可以运用这个模型去设计应用架构，将不同的工作分配给不同的 进程类型 。例如，HTTP 请求可以交给 web 进程来处理，而常驻的后台工作则交由 worker 进程负责。</p><p>这并不包括个别较为特殊的进程，例如通过虚拟机的线程处理并发的内部运算，或是使用诸如 EventMachine, Twisted, Node.js 的异步/事件触发模型。但一台独立的虚拟机的扩展有瓶颈（垂直扩展），所以应用程序必须可以在多台物理机器间跨进程工作。</p><p>上述进程模型会在系统急需扩展时大放异彩。 12-Factor 应用的进程所具备的无共享，水平分区的特性 意味着添加并发会变得简单而稳妥。这些进程的类型以及每个类型中进程的数量就被称作 进程构成 。</p><p>12-Factor 应用的进程 不需要守护进程 或是写入 PID 文件。相反的，应该借助操作系统的进程管理器(例如 systemd ，分布式的进程管理云平台，或是类似 Foreman 的工具)，来管理 输出流 ，响应崩溃的进程，以及处理用户触发的重启和关闭超级进程的请求。</p><h3 id="IX-易处理——快速启动和优雅终止可最大化健壮性"><a href="#IX-易处理——快速启动和优雅终止可最大化健壮性" class="headerlink" title="IX. 易处理——快速启动和优雅终止可最大化健壮性"></a>IX. 易处理——快速启动和优雅终止可最大化健壮性</h3><p>12-Factor 应用的 进程 是 <strong>易处理</strong>（disposable）的，意思是说它们可以<strong>瞬间开启或停止</strong>。 这有利于快速、弹性的伸缩应用，迅速部署变化的 代码 或 配置 ，稳健的部署应用。</p><p>进程应当追求 最小启动时间 。 理想状态下，进程从敲下命令到真正启动并等待请求的时间应该只需很短的时间。更少的启动时间提供了更敏捷的 发布 以及扩展过程，此外还增加了健壮性，因为进程管理器可以在授权情形下容易的将进程搬到新的物理机器上。</p><p>进程 一旦接收 <strong>终止信号</strong>（SIGTERM） 就会优雅的终止 。就网络进程而言，优雅终止是指停止监听服务的端口，即拒绝所有新的请求，并继续执行当前已接收的请求，然后退出。此类型的进程所隐含的要求是HTTP请求大多都很短(不会超过几秒钟)，而在长时间轮询中，客户端在丢失连接后应该马上尝试重连。</p><p>对于 worker 进程来说，优雅终止是指将当前任务退回队列。例如，RabbitMQ 中，worker 可以发送一个NACK信号。 Beanstalkd 中，任务终止并退回队列会在worker断开时自动触发。有锁机制的系统诸如 Delayed Job 则需要确定释放了系统资源。此类型的进程所隐含的要求是，任务都应该 可重复执行 ， 这主要由将结果包装进事务或是使重复操作 幂等 来实现。</p><p>进程还应当在面对突然死亡时保持健壮，例如底层硬件故障。虽然这种情况比起优雅终止来说少之又少，但终究有可能发生。一种推荐的方式是使用一个健壮的后端队列，例如 Beanstalkd ，它可以在客户端断开或超时后自动退回任务。无论如何，12-Factor 应用都应该可以设计能够应对意外的、不优雅的终结。Crash-only design 将这种概念转化为 合乎逻辑的理论。</p><h3 id="X-开发环境与线上环境等价——尽可能的保持开发，预发布，线上环境相同"><a href="#X-开发环境与线上环境等价——尽可能的保持开发，预发布，线上环境相同" class="headerlink" title="X. 开发环境与线上环境等价——尽可能的保持开发，预发布，线上环境相同"></a>X. 开发环境与线上环境等价——尽可能的保持开发，预发布，线上环境相同</h3><p>从以往经验来看，开发环境（即开发人员的本地 部署）和线上环境（外部用户访问的真实部署）之间存在着很多差异。这些差异表现在以下三个方面：</p><ul><li>时间差异： 开发人员正在编写的代码可能需要几天，几周，甚至几个月才会上线。</li><li>人员差异： 开发人员编写代码，运维人员部署代码。</li><li><p>工具差异： 开发人员或许使用 Nginx，SQLite，OS X，而线上环境使用 Apache，MySQL 以及 Linux。<br>12-Factor 应用想要做到 <strong>持续部署</strong> 就必须<strong>缩小本地与线上差异</strong>。 再回头看上面所描述的三个差异:</p></li><li><p>缩小时间差异：开发人员可以几小时，甚至几分钟就部署代码。</p></li><li>缩小人员差异：开发人员不只要编写代码，更应该密切参与部署过程以及代码在线上的表现。</li><li>缩小工具差异：尽量保证开发环境以及线上环境的一致性。<br>将上述总结变为一个表格如下：</li></ul><table><thead><tr><th style="text-align:left">Item</th><th style="text-align:right">传统应用</th><th style="text-align:center">12-Factor 应用</th></tr></thead><tbody><tr><td style="text-align:left">每次部署间隔</td><td style="text-align:right">数周</td><td style="text-align:center">几个小时</td></tr><tr><td style="text-align:left">开发人员 vs 运维人员</td><td style="text-align:right">不同的人</td><td style="text-align:center">相同的人</td></tr><tr><td style="text-align:left">开发环境 vs 线上环境</td><td style="text-align:right">不同</td><td style="text-align:center">尽量接近</td></tr></tbody></table><p>后端服务 是保持开发与线上等价的重要部分，例如数据库，队列系统，以及缓存。许多语言都提供了简化获取后端服务的类库，例如不同类型服务的 适配器 。下列表格提供了一些例子。</p><table><thead><tr><th style="text-align:left">类型</th><th style="text-align:right">语言</th><th style="text-align:center">类库</th><th style="text-align:center">适配器</th></tr></thead><tbody><tr><td style="text-align:left">数据库</td><td style="text-align:right">Ruby/Rails</td><td style="text-align:center">ActiveRecord</td><td style="text-align:center">MySQL, PostgreSQL, SQLite</td></tr><tr><td style="text-align:left">队列</td><td style="text-align:right">Python/Django</td><td style="text-align:center">Celery</td><td style="text-align:center">RabbitMQ, Beanstalkd, Redis</td></tr><tr><td style="text-align:left">缓存</td><td style="text-align:right">Ruby/Rails</td><td style="text-align:center">ActiveSupport::Cache</td><td style="text-align:center">Memory, filesystem,Memcached</td></tr></tbody></table><p>开发人员有时会觉得在本地环境中使用轻量的后端服务具有很强的吸引力，而那些更重量级的健壮的后端服务应该使用在生产环境。例如，本地使用 SQLite 线上使用 PostgreSQL；又如本地缓存在进程内存中而线上存入 Memcached。</p><p>12-Factor 应用的开发人员应该反对在不同环境间使用不同的后端服务 ，即使适配器已经可以几乎消除使用上的差异。这是因为，不同的后端服务意味着会突然出现的不兼容，从而导致测试、预发布都正常的代码在线上出现问题。这些错误会给持续部署带来阻力。从应用程序的生命周期来看，消除这种阻力需要花费很大的代价。</p><p>与此同时，轻量的本地服务也不像以前那样引人注目。借助于Homebrew，apt-get等现代的打包系统，诸如Memcached、PostgreSQL、RabbitMQ 等后端服务的安装与运行也并不复杂。此外，使用类似 Chef 和 Puppet 的声明式配置工具，结合像 Vagrant 这样轻量的虚拟环境就可以使得开发人员的本地环境与线上环境无限接近。与同步环境和持续部署所带来的益处相比，安装这些系统显然是值得的。</p><p>不同后端服务的适配器仍然是有用的，因为它们可以使移植后端服务变得简单。但应用的所有部署，这其中包括开发、预发布以及线上环境，都应该使用同一个后端服务的相同版本。</p><h3 id="XI-日志——把日志当作事件流"><a href="#XI-日志——把日志当作事件流" class="headerlink" title="XI. 日志——把日志当作事件流"></a>XI. 日志——把日志当作事件流</h3><p><strong>日志</strong> 使得应用程序运行的动作变得透明。在基于服务器的环境中，日志通常被写在硬盘的一个文件里，但这只是一种输出格式。</p><p>日志应该是 <strong>事件流</strong> 的汇总，将所有运行中进程和后端服务的输出流按照时间顺序收集起来。尽管在回溯问题时可能需要看很多行，日志最原始的格式确实是一个事件一行。日志没有确定开始和结束，但随着应用在运行会持续的增加。</p><p>12-factor应用本身从不考虑存储自己的输出流。 不应该试图去写或者管理日志文件。相反，每一个运行的进程都会直接的标准输出（stdout）事件流。开发环境中，开发人员可以通过这些数据流，实时在终端看到应用的活动。</p><p>在预发布或线上部署中，每个进程的输出流由运行环境截获，并将其他输出流整理在一起，然后一并发送给一个或多个最终的处理程序，用于查看或是长期存档。这些存档路径对于应用来说不可见也不可配置，而是完全交给程序的运行环境管理。类似 Logplex 和 Fluentd 的开源工具可以达到这个目的。</p><p>这些事件流可以输出至文件，或者在终端实时观察。最重要的，输出流可以发送到 Splunk 这样的日志索引及分析系统，或 Hadoop/Hive 这样的通用数据存储系统。这些系统为查看应用的历史活动提供了强大而灵活的功能，包括：</p><ul><li>找出过去一段时间特殊的事件。</li><li>图形化一个大规模的趋势，比如每分钟的请求量。</li><li>根据用户定义的条件实时触发警报，比如每分钟的报错超过某个警戒线。</li></ul><h3 id="XII-管理进程——后台管理任务当作一次性进程运行"><a href="#XII-管理进程——后台管理任务当作一次性进程运行" class="headerlink" title="XII. 管理进程——后台管理任务当作一次性进程运行"></a>XII. 管理进程——后台管理任务当作一次性进程运行</h3><p><strong>进程构成</strong>（process formation）是指用来处理应用的常规业务（比如处理 web 请求）的一组进程。与此不同，开发人员经常希望执行一些管理或维护应用的一次性任务，例如：</p><ul><li>运行数据移植（Django 中的 manage.py migrate, Rails 中的 rake db:migrate）。</li><li>运行一个控制台（也被称为 REPL shell），来执行一些代码或是针对线上数据库做一些检查。大多数语言都通过解释器提供了一个 REPL 工具（python 或 perl） ，或是其他命令（Ruby 使用 irb, Rails 使用 rails console）。</li><li>运行一些提交到代码仓库的一次性脚本。</li></ul><p>一次性管理进程应该和正常的 <strong>常驻进程</strong> 使用同样的环境。这些管理进程和任何其他的进程一样使用相同的 代码 和 配置 ，基于某个 <strong>发布版本</strong> 运行。后台管理代码应该随其他应用程序代码一起发布，从而避免同步问题。</p><p>所有进程类型应该使用同样的 <strong>依赖隔离</strong> 技术。例如，如果Ruby的web进程使用了命令 bundle exec thin start ，那么数据库移植应使用 bundle exec rake db:migrate 。同样的，如果一个 Python 程序使用了 Virtualenv，则需要在运行 Tornado Web 服务器和任何 manage.py 管理进程时引入 bin/python 。</p><p>12-factor 尤其青睐那些提供了 REPL shell 的语言，因为那会让运行一次性脚本变得简单。在本地部署中，开发人员直接在命令行使用 shell 命令调用一次性管理进程。在线上部署中，开发人员依旧可以使用ssh或是运行环境提供的其他机制来运行这样的进程。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;使用标准化流程自动配置，从而使新的开发者花费&lt;strong&gt;最少的学习成本&lt;/strong&gt;加入这个项目。&lt;/li&gt;
&lt;li&gt;和操作系统之间尽可能的划清界限，在各个系统中提供最大的&lt;strong&gt;可移植性&lt;/strong&gt;。&lt;/li&gt;

      
    
    </summary>
    
      <category term="微服务" scheme="https://challange.github.io/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
    
      <category term="微服务" scheme="https://challange.github.io/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
  </entry>
  
  <entry>
    <title>Java基础学习——Lambda表达式</title>
    <link href="https://challange.github.io/DIY-DIY%E2%80%94%E2%80%94Lambda%E8%A1%A8%E8%BE%BE%E5%BC%8F/"/>
    <id>https://challange.github.io/DIY-DIY——Lambda表达式/</id>
    <published>2019-05-06T08:05:05.000Z</published>
    <updated>2019-05-08T16:09:30.086Z</updated>
    
    <content type="html"><![CDATA[<h3 id="函数式编程是什么"><a href="#函数式编程是什么" class="headerlink" title="函数式编程是什么"></a>函数式编程是什么</h3><blockquote><p>函数式编程，是一种使用函数进行编程的方式，一个“函数”对应于一个数学函数:它接受零个或多个参数，生成一个或多个结果，并且不会有任何副作用，函数式函数无论在何处、何时、何地对于同样的输入总会返回相同的结果。</p></blockquote><h3 id="一、函数式编程优劣势对比"><a href="#一、函数式编程优劣势对比" class="headerlink" title="一、函数式编程优劣势对比"></a>一、函数式编程优劣势对比</h3><h4 id="匿名类与Lambda表达式"><a href="#匿名类与Lambda表达式" class="headerlink" title="匿名类与Lambda表达式"></a>匿名类与Lambda表达式</h4><p>代码简洁，相较于匿名内部类，Lambda表达式大大简化了代码量,代码可读性也会更好<br><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></pre></td><td class="code"><pre><span class="line">Runnable r1 = <span class="keyword">new</span> Runnable()&#123;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span>&#123;</span><br><span class="line">System.out.println(<span class="string">"Hello"</span>);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">Runnable r2 = () -&gt; System.out.println(<span class="string">"hello"</span>);</span><br></pre></td></tr></table></figure></p><p><strong>局限</strong>：</p><ol><li>匿名类与lambda表达式中的this和super含义是不同的，在匿名类中，this代表自身，而lambda代表的是包含类。</li><li><p>匿名类可以屏蔽包含类的变量，而lambda不能，但是与包含类使用相同的变量，变量就容易产生歧义，难于理解。</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">int a = 10;</span><br><span class="line">Runnable r1 = () -&gt; &#123;</span><br><span class="line">//   int a = 10;  // 编译错误</span><br><span class="line">     int a1 = 10;</span><br><span class="line">     System.out.println(a1);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li><li><p>在涉及重载的方法，Lambda表达式可能会导致模棱两可，但可以通过强制类型转换来解决。</p><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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Task</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line">    </span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">doSomething</span><span class="params">(Runnable r)</span></span>&#123;</span><br><span class="line">    r.run();</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">doSomething</span><span class="params">(Task r)</span></span>&#123;</span><br><span class="line">    r.execute();</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// Error:(19, 9) java: 对doSomething的引用不明确</span></span><br><span class="line"><span class="comment">// doSomething(() -&gt; System.out.println("1234"));</span></span><br><span class="line">    doSomething((Task) () -&gt; System.out.println(<span class="string">"1234"</span>));</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="行为参数化"><a href="#行为参数化" class="headerlink" title="行为参数化"></a>行为参数化</h4><p><strong>Lambda表达式引入了将方法作为参数传递的能力</strong>，在环绕场景下（一个方法只有中间部分逻辑不一样），增加了方法的复用，减少代码冗余。结合泛型理解，泛型使类或方法可以复用于更多的变量类型， 而函数式则进一步拓展了方法的复用性。<br><strong>局限</strong>：</p><ol><li>提供行为参数化后，如果行为较为复杂，则很难一眼看出行为的含义，这时候就会降低代码的可读性，相较而言，命名规范的函数则有见名知意的好处。</li></ol><h4 id="无副作用纯函数，引用透明"><a href="#无副作用纯函数，引用透明" class="headerlink" title="无副作用纯函数，引用透明"></a>无副作用纯函数，引用透明</h4><p>所谓共享数据就是数据可能被多个方法读取更新，在并发使用数据的时候必须通过上锁来确保线程安全。函数式编程所倡导的避免共享可变数据，只要参数确定就一定会返回确定结果，增加程序的可控性，不用考虑复杂易错的锁机制，使并行更加容易，充分利用计算机多核优势。</p><p><img src="/images/DIY——Lambda表达式/1.png" alt="Alt text"><br>为了维持不可变性，“函数式”的函数或者方法都只能修改本地变量，并且它引用的对象都应是不可变对象.。</p><p><strong>局限</strong>：</p><ol><li>为了确保避免共享可变数据引入——增加定义变量与赋值——增大了空间的使用</li></ol><h4 id="异常"><a href="#异常" class="headerlink" title="异常"></a>异常</h4><p>函数式要求函数或者方法不应抛出任何异常，因为一旦抛出异常，结果就被终止了；类比于数学函数，传入一个<strong>合法</strong>的参数，一定会返回一个确定的结果。在不使用异常的情况下，Java8引入了Optional&lt; T&gt;类型来承载异常情况，如果异常不能返回结果则返回一个空的optional对象。</p><h4 id="声明式编程"><a href="#声明式编程" class="headerlink" title="声明式编程"></a>声明式编程</h4><p>经典的面向对象编程我们专注于<strong>如何实现</strong>，思维模式为：“首先做这个，紧接着更新那个，然后……”，面向对象是抽象对象、对象之间的交互；而函数式编程更关注与<strong>要做什么</strong>，Stream流是典型的应用，采用这种“要做什么”风格的编程就是声明式编程，编程者考虑的是指定规则，而由系统或者封装来觉得如何实现这个目标。这样带来的好处是<strong>让代码更加接近于问题陈述</strong>。</p><p>习惯于声明式编程思维，我们可以更容易的利用化归思想，将复杂问题拆分为若干小问题逐步解决，自顶向下，开始更加关注于函数的输入以及输出结果，而不是过早的考虑如何做、修改哪些东西。</p><h4 id="高阶函数与科里化"><a href="#高阶函数与科里化" class="headerlink" title="高阶函数与科里化"></a>高阶函数与科里化</h4><p>满足接受一个或多个函数作为参数或返回结果是一个函数的函数都是高阶函数，高阶函数提供了链式调用的功能。</p><p>科里化是一种将具备2个参数（比如，x和y）的函数f转化为使用一个参数的函数g，并且这个函数的返回值也是一个函数，它可以作为新的函数的一个参数。后者返回值与初始函数返回值，f(x,y)=(g(x))y</p><p>高阶函数与科里化也使声明式编程可以更加运用自如。</p><h4 id="延迟计算与惰性求值"><a href="#延迟计算与惰性求值" class="headerlink" title="延迟计算与惰性求值"></a>延迟计算与惰性求值</h4><p>延迟计算以Stream为例，向Stream发起的一系列中间操作会先被一一保存起来，直到发起一个终端操作（Stream分为中间操作和中端操作，中间操作返回一个Stream，终端操作从流水线生成结果），才会进行实际的计算。延迟计算与惰性求值使得代码具备了巨大的优化潜能。支持惰性求值的编译器会像数学家看待代数表达式那样看待函数式程序:抵消相同项从而避免执行无谓的代码，优化代码的执行顺序从而实现更高的执行效率甚至是减少错误。<br>局限</p><ol><li>惰性求值最大的问题还是惰性，现实世界中很多问题还是需要严格求值的，需要严格的顺序执行，如System.nextLine()每次会读取下一行记录</li><li></li></ol><h4 id="测试与调试"><a href="#测试与调试" class="headerlink" title="测试与调试"></a>测试与调试</h4><blockquote><p>许多同学提到函数式程序难调试，个人觉得并不是因为难调试，而是我们要掌握调试的方法和工具</p></blockquote><ol><li><strong>单元测试</strong>：因为函数式程序无副作用的特性使得单元测试更加容易，唯一需要做的就是传递一些可以代表边界条件的参数给这些函数并返回确定的结果，并且不会受其他因素干扰，如调用顺序、外部状态干扰等。<br>Lambda没有函数名，的确带来了测试困难，这时可以借助某个字段访问Lambda函数来测试函数内封装的逻辑，可能又会问每个表达式定义一个变量做测试也太麻烦了，从声明式编程的角度出发，我们应该关注的是一个方法的可靠性，每个lambda仅仅是函数的实现细节，当放在函数内整体测试。</li><li><strong>Java Stream Debugger</strong><br>工欲善其事必先利其器，推荐个调试插件<strong>Java Stream Debugger</strong>，可以以可视化的形式展现stream筛选过滤的过程。<br><img src="/images/DIY——Lambda表达式/2.png" alt="Alt text"></li><li><strong>日志输出</strong><br>使用peek方法可以在stream输Alt text出当前执行的元素，便于判断问题。<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></pre></td><td class="code"><pre><span class="line">List&lt;People&gt; list = peopleList.stream()</span><br><span class="line">            .peek(x -&gt; System.out.println(<span class="string">"frist:"</span>+x))</span><br><span class="line">            .filter(item -&gt; item != <span class="keyword">null</span>)</span><br><span class="line">            .peek(x -&gt; System.out.println(<span class="string">"second:"</span>+x))</span><br><span class="line">            .filter(item -&gt; item.getAge() &gt; <span class="number">5</span>)</span><br><span class="line">            .peek(x -&gt; System.out.println(<span class="string">"three:"</span>+x))</span><br><span class="line">            .collect(Collectors.toList());</span><br></pre></td></tr></table></figure></li></ol><h4 id="性能"><a href="#性能" class="headerlink" title="性能"></a>性能</h4><ol><li>在Java7引入了InvokeDynamic指令，用于支持在JVM上运行动态类型语言，Lambda表达式使用InvokeDynamic指令使表达式转化为字节码推迟到了<strong>运行时</strong>，避免了静态初始化，生成大量的匿名类，由此带来的问题，函数首次运行需要先进行编译，也就造成首次运行可能会占用较长时间，因此需要注意预热。</li><li>包装类型转换往往不易发现，需要重点关注</li><li>总的来说lambda不会对程序性能带来提升，甚至有可能性能下降，但是我们还是得拥抱它，因为它在多核并行计算、代码可读性、可拓展行上的优势足以抵消它降低的性能。</li></ol><h4 id="其他问题："><a href="#其他问题：" class="headerlink" title="其他问题："></a>其他问题：</h4><ol><li>增加了使用门槛，但是工欲善其事必先利其器，作为一个合格Java程序猿本身就应具备持续的学习能力</li></ol><h3 id="二、Stream流的哪一个方法最有价值，为什么？"><a href="#二、Stream流的哪一个方法最有价值，为什么？" class="headerlink" title="二、Stream流的哪一个方法最有价值，为什么？"></a>二、Stream流的哪一个方法最有价值，为什么？</h3><blockquote><p>同学们为自己心目中最有价值的Stream流方法进行了投票，投票结果来看最受欢迎的三个方法是filter、map、collect，filter主要因为可以进行条件过滤，map方法则可以映射每个元素生成新的元素，collect则是最后收集元素必不可少的一环，这三个方法无疑是Stream流操作最常用的方法。</p></blockquote><p><img src="/images/DIY——Lambda表达式/3.png" alt="Alt text"></p><p>简单讲一下流的定义：<strong>从支持数据处理的源生成的元素序列</strong>。</p><blockquote><ul><li><strong>元素序列</strong>——流提供了可以访问特定元素类型的一组有序值的接口。流的目的在于表达计算，比如前面见到的 filter、sorted和map。</li><li><strong>源</strong>——流会使用一个提供数据的源，如集合、数组或输入/输出资源。</li><li><strong>数据处理操作</strong>——流的数据处理功能支持类似于数据库的操作，以及函数式编程语言中 的常用操作，如filter、map、reduce、find、match、sort等。流操作可以顺序执行，也可并行执行。</li></ul></blockquote><p>流的另外两个特点</p><blockquote><ul><li><strong>流水线</strong>——很多流操作本身会返回一个流，这样多个操作就可以链接起来，形成一个大<br>的流水线。流水线背后其实是一种建造者模式</li><li><strong>内部迭代</strong>——与使用迭代器显式迭代的集合不同，流的迭代操作是在背后进行的。</li></ul></blockquote><p>流的使用一般包括三件事:</p><blockquote><ul><li>一个数据源(如集合)来执行一个查询;</li><li>一个中间操作链，形成一条流的流水线;</li><li>一个终端操作，执行流水线，并能生成结果。</li></ul></blockquote><p><strong>parallelStream与parallel区别</strong>：<br>parallelStream是Collection接口定义的方法，在stream构造时传入参数使用parallel生成并行流。parallel是BaseStream的一个方法，能够将一个顺序流转化为一个并行流，因此在执行parallel前可以以顺序流先进行预处理。<br>如：<br><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">public static long parallelSum(long n) &#123;</span><br><span class="line">        return Stream.iterate(1L, i -&gt; i + 1)</span><br><span class="line">                     .limit(n)</span><br><span class="line">                     .parallel()      // 转化为并行流</span><br><span class="line">                     .reduce(0L, Long::sum);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>Stream流方法的使用<img src="/images/DIY——Lambda表达式/4.png" alt="Alt text"></p><p>参考文章：<br><a href="https://www.kancloud.cn/kancloud/functional-programm-for-rest/56930">傻瓜函数式编程</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;函数式编程是什么&quot;&gt;&lt;a href=&quot;#函数式编程是什么&quot; class=&quot;headerlink&quot; title=&quot;函数式编程是什么&quot;&gt;&lt;/a&gt;函数式编程是什么&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;函数式编程，是一种使用函数进行编程的方式，一个“函数”对应于一个数
      
    
    </summary>
    
      <category term="Java基础" scheme="https://challange.github.io/categories/Java%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="DIY" scheme="https://challange.github.io/tags/DIY/"/>
    
      <category term="Lambda表达式" scheme="https://challange.github.io/tags/Lambda%E8%A1%A8%E8%BE%BE%E5%BC%8F/"/>
    
      <category term="Java基础" scheme="https://challange.github.io/tags/Java%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>SpringCloud——Gateway跨域配置</title>
    <link href="https://challange.github.io/springcloud-SpringCloud%E2%80%94%E2%80%94Gateway%E8%B7%A8%E5%9F%9F%E9%85%8D%E7%BD%AE/"/>
    <id>https://challange.github.io/springcloud-SpringCloud——Gateway跨域配置/</id>
    <published>2019-05-05T08:05:05.000Z</published>
    <updated>2019-05-05T08:15:10.444Z</updated>
    
    <content type="html"><![CDATA[<h3 id="正确配置方法"><a href="#正确配置方法" class="headerlink" title="正确配置方法"></a>正确配置方法</h3><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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 配置跨域</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> CorsWebFilter <span class="title">corsFilter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    CorsConfiguration config = <span class="keyword">new</span> CorsConfiguration();</span><br><span class="line">    <span class="comment">// cookie跨域</span></span><br><span class="line">    config.setAllowCredentials(Boolean.TRUE);</span><br><span class="line">    config.addAllowedMethod(<span class="string">"*"</span>);</span><br><span class="line">    config.addAllowedOrigin(<span class="string">"*"</span>);</span><br><span class="line">    config.addAllowedHeader(<span class="string">"*"</span>);</span><br><span class="line">    <span class="comment">// 配置前端js允许访问的自定义响应头</span></span><br><span class="line">    config.addExposedHeader(<span class="string">"setToken"</span>);</span><br><span class="line"></span><br><span class="line">    UrlBasedCorsConfigurationSource source = <span class="keyword">new</span> UrlBasedCorsConfigurationSource(<span class="keyword">new</span> PathPatternParser());</span><br><span class="line">    source.registerCorsConfiguration(<span class="string">"/**"</span>, config);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> CorsWebFilter(source);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="配置不生效方法"><a href="#配置不生效方法" class="headerlink" title="配置不生效方法"></a>配置不生效方法</h3><ul><li><a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RELEASE/single/spring-cloud-gateway.html#_cors_configuration">官方推荐配置</a></li><li>自定义实现GlobalFilter无效</li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;正确配置方法&quot;&gt;&lt;a href=&quot;#正确配置方法&quot; class=&quot;headerlink&quot; title=&quot;正确配置方法&quot;&gt;&lt;/a&gt;正确配置方法&lt;/h3&gt;&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutt
      
    
    </summary>
    
      <category term="SpringCloud" scheme="https://challange.github.io/categories/SpringCloud/"/>
    
    
      <category term="SpringCloud" scheme="https://challange.github.io/tags/SpringCloud/"/>
    
      <category term="Gateway" scheme="https://challange.github.io/tags/Gateway/"/>
    
  </entry>
  
  <entry>
    <title>SpringCloud——Gateway跨域配置</title>
    <link href="https://challange.github.io/springcloud-SpringCloud%E2%80%94%E2%80%94Gateway%E4%BD%BF%E7%94%A8redis%E5%AE%9E%E7%8E%B0%E5%8A%A8%E6%80%81%E8%B7%AF%E7%94%B1/"/>
    <id>https://challange.github.io/springcloud-SpringCloud——Gateway使用redis实现动态路由/</id>
    <published>2019-05-05T08:05:05.000Z</published>
    <updated>2019-06-06T09:52:27.119Z</updated>
    
    <summary type="html">
    
    </summary>
    
      <category term="SpringCloud" scheme="https://challange.github.io/categories/SpringCloud/"/>
    
    
      <category term="SpringCloud" scheme="https://challange.github.io/tags/SpringCloud/"/>
    
      <category term="Gateway" scheme="https://challange.github.io/tags/Gateway/"/>
    
  </entry>
  
  <entry>
    <title>SpringCloud——Gateway配置大全</title>
    <link href="https://challange.github.io/springcloud-SpringCloud%E2%80%94%E2%80%94Gateway%E9%85%8D%E7%BD%AE%E5%A4%A7%E5%85%A8/"/>
    <id>https://challange.github.io/springcloud-SpringCloud——Gateway配置大全/</id>
    <published>2019-04-07T08:36:59.000Z</published>
    <updated>2019-04-07T08:44:25.545Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>了解Gateway的配置才可以理解使用Gateway可以做什么事情，才能更好地应用在产品开发中。</p></blockquote><h3 id="Predicates"><a href="#Predicates" class="headerlink" title="Predicates"></a>Predicates</h3><blockquote><p>Predicates主要起的作用是：配置路由匹配请求的规则</p></blockquote><h4 id="Http相关"><a href="#Http相关" class="headerlink" title="Http相关"></a>Http相关</h4><h5 id="Path"><a href="#Path" class="headerlink" title="Path"></a>Path</h5><blockquote><p>配置对于请求路径的匹配规则</p></blockquote><ol><li><p>yml配置，多个参数用逗号隔开</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">Path</span> <span class="string">=</span> <span class="string">/aa/**,/bb/**</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"Path"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"pattern"</span>:<span class="string">"/aa/**"</span>,<span class="attr">"pattern1"</span>:<span class="string">"/bb/**"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h5 id="Cookie"><a href="#Cookie" class="headerlink" title="Cookie"></a>Cookie</h5><blockquote><p>配置对Cookie中值的匹配，第一个为key，第二个为value。下例匹配cookie设置chocolate:ch.p的请求</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">Cookie</span> <span class="string">=</span> <span class="string">chocolate,ch.p</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"Cookie"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"chocolate"</span>,<span class="attr">"_genkey_1"</span>:<span class="string">"ch.p"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h5 id="Header"><a href="#Header" class="headerlink" title="Header"></a>Header</h5><blockquote><p>匹配Http请求中设置的内容，http-header设置X-Request-Id:\d+可以匹配，第二个参数第二个参数是正则表达式</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">Header</span> <span class="string">=</span> <span class="string">X-Request-Id,\d+</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"Header"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"X-Request-Id"</span>,<span class="attr">"_genkey_1"</span>:<span class="string">"\d+"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h5 id="Host"><a href="#Host" class="headerlink" title="Host"></a>Host</h5><blockquote><p>匹配Http请求Host，匹配所有host为**.somehost.com的请求</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">Host</span> <span class="string">=</span> <span class="string">**.somehost.com</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"Host"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"**.somehost.com"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h5 id="Method"><a href="#Method" class="headerlink" title="Method"></a>Method</h5><blockquote><p>匹配Http请求头</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">Method</span> <span class="string">=</span> <span class="string">GET</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"Method"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"GET"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h5 id="Query"><a href="#Query" class="headerlink" title="Query"></a>Query</h5><blockquote><p>匹配Http请求中的查询参数，请求中携带param1=value的请求可以匹配</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">Query</span> <span class="string">=</span> <span class="string">param1,value</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"Query"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"param1"</span>,<span class="attr">"_genkey_1"</span>:<span class="string">"value"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h5 id="RemoteAddr"><a href="#RemoteAddr" class="headerlink" title="RemoteAddr"></a>RemoteAddr</h5><blockquote><p>匹配请求中的RemoteAddr</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">RemoteAddr</span> <span class="string">=</span> <span class="number">192.168</span><span class="number">.1</span><span class="number">.1</span><span class="string">/24</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"RemoteAddr"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"192.168.1.1/24"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="时间相关"><a href="#时间相关" class="headerlink" title="时间相关"></a>时间相关</h4><h5 id="After"><a href="#After" class="headerlink" title="After"></a>After</h5><blockquote><p>设置时间之后可以访问</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">After</span> <span class="string">=</span> <span class="number">2017</span><span class="bullet">-01</span><span class="bullet">-20</span><span class="attr">T17:42:47.789-07:00[America/Denver]</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"After"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"2017-01-20T17:42:47.789-07:00[America/Denver]"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h5 id="Before"><a href="#Before" class="headerlink" title="Before"></a>Before</h5><blockquote><p>设置时间之前可以访问</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">Before</span> <span class="string">=</span> <span class="number">2017</span><span class="bullet">-01</span><span class="bullet">-20</span><span class="attr">T17:42:47.789-07:00[America/Denver]</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"Before"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"2017-01-20T17:42:47.789-07:00[America/Denver]"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h5 id="Before-1"><a href="#Before-1" class="headerlink" title="Before"></a>Before</h5><blockquote><p>设置时间段内可以访问</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">Between</span> <span class="string">=</span> <span class="number">2017</span><span class="bullet">-01</span><span class="bullet">-20</span><span class="attr">T17:42:47.789-07:00[America/Denver],2017-01-21T17:42:47.789-07:00[America/Denver]</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"Between"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"2017-01-20T17:42:47.789-07:00[America/Denver]"</span>，<span class="string">"_genkey_1"</span>:<span class="string">"2017-01-21T17:42:47.789-07:00[America/Denver]"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="权重路由"><a href="#权重路由" class="headerlink" title="权重路由"></a>权重路由</h4><blockquote><p>至少两组以上路由可以配置权重路由，配置后会根据权重随机访问几个路由</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">Weight</span> <span class="string">=</span> <span class="string">service1,80</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"Weight"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"service1"</span>,<span class="attr">"_genkey_1"</span>:<span class="string">"80"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h3 id="Filters"><a href="#Filters" class="headerlink" title="Filters"></a>Filters</h3><h4 id="路径重写"><a href="#路径重写" class="headerlink" title="路径重写"></a>路径重写</h4><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">RewritePath</span> <span class="string">=</span> <span class="string">/path/(?&lt;segment&gt;.*),</span> <span class="string">/$\&#123;segment&#125;</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"RewritePath"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"/foo/(?&lt;segment&gt;.*)"</span>,<span class="attr">"_genkey_1"</span>:<span class="string">"/$\\&#123;segment&#125;"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="修改请求头"><a href="#修改请求头" class="headerlink" title="修改请求头"></a>修改请求头</h4><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">AddRequestHeader</span> <span class="string">=</span> <span class="string">X-Request-Foo,Bar</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"AddRequestHeader"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"X-Request-Foo"</span>,<span class="attr">"_genkey_1"</span>:<span class="string">"Bar"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="修改请求参数"><a href="#修改请求参数" class="headerlink" title="修改请求参数"></a>修改请求参数</h4><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">AddRequestParameter</span> <span class="string">=</span> <span class="string">foo,bar</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"AddRequestParameter"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"foo"</span>,<span class="attr">"_genkey_1"</span>:<span class="string">"bar"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="修改响应参数"><a href="#修改响应参数" class="headerlink" title="修改响应参数"></a>修改响应参数</h4><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">AddResponseHeader</span> <span class="string">=</span> <span class="string">X-Request-Foo,Bar</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"AddResponseHeader"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"X-Request-Foo"</span>,<span class="attr">"_genkey_1"</span>:<span class="string">"Bar"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="路径前缀增强"><a href="#路径前缀增强" class="headerlink" title="路径前缀增强"></a>路径前缀增强</h4><blockquote><p>请求路径/hello, 将会被替换为 /mypath/hello</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">PrefixPath</span> <span class="string">=</span> <span class="string">/mypath</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"PrefixPath"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"/mypath"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="路径前缀删除"><a href="#路径前缀删除" class="headerlink" title="路径前缀删除"></a>路径前缀删除</h4><blockquote><p>请求/name/bar/foo，去除掉前面两个前缀之后，最后转发到目标服务的路径为/foo</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">StripPrefix</span> <span class="string">=</span> <span class="number">2</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"StripPrefix"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"2"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="请求携带保留原始Host"><a href="#请求携带保留原始Host" class="headerlink" title="请求携带保留原始Host"></a>请求携带保留原始Host</h4><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">PreserveHostHeader</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"PreserveHostHeader"</span>,<span class="attr">"args"</span>:&#123;&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="重定向"><a href="#重定向" class="headerlink" title="重定向"></a>重定向</h4><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">RedirectTo</span> <span class="string">=</span> <span class="number">302</span><span class="string">,http://acme.org</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"RedirectTo"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"302"</span>,<span class="attr">"_genkey_1"</span>:<span class="string">"http://acme.org"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="断路器"><a href="#断路器" class="headerlink" title="断路器"></a>断路器</h4><ol><li><p>yml配置</p><figure class="highlight yml"><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></pre></td><td class="code"><pre><span class="line"><span class="attr">- name:</span> <span class="string">Hystrix</span></span><br><span class="line"><span class="attr">  args:</span></span><br><span class="line">  <span class="comment"># 断路后跳转地址</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">fallbackcmd</span></span><br><span class="line"><span class="attr">      fallbackUri:</span> <span class="attr">forward:/incaseoffailureusethis</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"Hystrix"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"name"</span>:<span class="string">"fallbackcmd"</span>,<span class="attr">"fallbackUri"</span>:<span class="string">"forward:/incaseoffailureusethis"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="集成Redis原生支持请求限流"><a href="#集成Redis原生支持请求限流" class="headerlink" title="集成Redis原生支持请求限流"></a>集成Redis原生支持请求限流</h4><ol><li><p>yml配置</p><figure class="highlight yml"><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="attr">- name:</span> <span class="string">RequestRateLimiter</span></span><br><span class="line"><span class="attr">  args:</span></span><br><span class="line">    <span class="string">redis-rate-limiter.replenishRate:</span> <span class="number">10</span>  </span><br><span class="line">    <span class="string">redis-rate-limiter.burstCapacity:</span> <span class="number">20</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"RequestRateLimiter"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"redis-rate-limiter.replenishRate"</span>:<span class="string">"10"</span>,<span class="attr">"redis-rate-limiter.burstCapacity"</span>:<span class="string">"20"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="删除请求头属性"><a href="#删除请求头属性" class="headerlink" title="删除请求头属性"></a>删除请求头属性</h4><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">RemoveRequestHeader</span> <span class="string">=</span> <span class="string">X-Request-Foo</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"RemoveRequestHeader"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"X-Request-Foo"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="删除响应头属性"><a href="#删除响应头属性" class="headerlink" title="删除响应头属性"></a>删除响应头属性</h4><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">RemoveResponseHeader</span> <span class="string">=</span> <span class="string">X-Request-Foo</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"RemoveResponseHeader"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"X-Request-Foo"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="重写响应头"><a href="#重写响应头" class="headerlink" title="重写响应头"></a>重写响应头</h4><blockquote><p>将请求 /42?user=ford&amp;password=omg!what&amp;flag=true, 改为 /42?user=ford&amp;password=***&amp;flag=true</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">RewriteResponseHeader</span> <span class="string">=</span> <span class="string">X-Response-Foo,password=[^&amp;]+,password=***</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"RewriteResponseHeader"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"X-Response-Foo"</span>,<span class="attr">"_genkey_1"</span>:<span class="string">"password=[^&amp;]+"</span>,<span class="attr">"_genkey_2"</span>:<span class="string">"password=***"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="重设请求路径"><a href="#重设请求路径" class="headerlink" title="重设请求路径"></a>重设请求路径</h4><blockquote><p>请求/foo/bar，在接下来的处理中被改为/bar</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">SetPath</span> <span class="string">=/&#123;segment&#125;</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"SetPath"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"/&#123;segment&#125;"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="设置响应头"><a href="#设置响应头" class="headerlink" title="设置响应头"></a>设置响应头</h4><blockquote><p>在接下来的处理中修改响应头X-Response-Foo为Bar</p></blockquote><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="string">SetResponseHeader</span> <span class="string">=X-Request-Foo,Bar</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"SetResponseHeader"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"X-Response-Foo"</span>,<span class="attr">"_genkey_1"</span>:<span class="string">"Bar"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="设置Http状态"><a href="#设置Http状态" class="headerlink" title="设置Http状态"></a>设置Http状态</h4><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">- name:</span> <span class="string">SetStatus</span></span><br><span class="line"><span class="attr">  args:</span></span><br><span class="line">  <span class="attr">status:</span> <span class="number">401</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"SetStatus"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"302"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="设置文件传输大小"><a href="#设置文件传输大小" class="headerlink" title="设置文件传输大小"></a>设置文件传输大小</h4><ol><li><p>yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">- name:</span> <span class="string">RequestSize</span></span><br><span class="line"><span class="attr">  args:</span></span><br><span class="line"><span class="attr">   maxSize:</span> <span class="number">5000000</span></span><br></pre></td></tr></table></figure></li><li><p>json配置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"name"</span>:<span class="string">"RequestSize"</span>,<span class="attr">"args"</span>:&#123;<span class="attr">"_genkey_0"</span>:<span class="string">"5000000"</span>&#125;&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="失败重试"><a href="#失败重试" class="headerlink" title="失败重试"></a>失败重试</h4><ol><li>yml配置<figure class="highlight yml"><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="attr">- name:</span> <span class="string">Retry</span></span><br><span class="line"><span class="attr">  args:</span></span><br><span class="line">  <span class="attr">retries:</span> <span class="number">3</span></span><br><span class="line"><span class="attr">      statuses:</span> <span class="string">BAD_GATEWAY</span></span><br></pre></td></tr></table></figure></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;了解Gateway的配置才可以理解使用Gateway可以做什么事情，才能更好地应用在产品开发中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;Predicates&quot;&gt;&lt;a href=&quot;#Predicates&quot; class=&quot;headerli
      
    
    </summary>
    
      <category term="SpringCloud" scheme="https://challange.github.io/categories/SpringCloud/"/>
    
    
      <category term="SpringCloud" scheme="https://challange.github.io/tags/SpringCloud/"/>
    
      <category term="Gateway" scheme="https://challange.github.io/tags/Gateway/"/>
    
  </entry>
  
  <entry>
    <title>从零开始玩转SpringCloud（二）：Gateway网关对接注册中心</title>
    <link href="https://challange.github.io/springcloud-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%8E%A9%E8%BD%ACSpringCloud%EF%BC%88%E4%BA%8C%EF%BC%89%EF%BC%9AGateway%E7%BD%91%E5%85%B3%E5%AF%B9%E6%8E%A5%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/"/>
    <id>https://challange.github.io/springcloud-从零开始玩转SpringCloud（二）：Gateway网关对接注册中心/</id>
    <published>2019-04-07T06:54:30.000Z</published>
    <updated>2019-04-07T06:58:46.810Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>简介：Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。</p></blockquote><h3 id="项目搭建"><a href="#项目搭建" class="headerlink" title="项目搭建"></a>项目搭建</h3><ol><li>引入依赖<figure class="highlight xml"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--Eureka 客户端--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-eureka-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!--Gateway 路由--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-gateway<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ol><blockquote><p>注意：不要引入spring-boot-starter-web包，会导致Gateway启动抛出异常，错误如下。因为Spring Cloud Gateway 是使用 netty+webflux实现，webflux与web是冲突的。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Consider defining a bean of type &apos;org.springframework.http.codec.ServerCodecConfigurer&apos; in your configuration.</span><br></pre></td></tr></table></figure></p></blockquote><ol start="2"><li><p>在Application中使用@EnableEurekaClient</p><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><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.gateway;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.SpringApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.cloud.netflix.eureka.EnableEurekaClient;</span><br><span class="line"></span><br><span class="line"><span class="meta">@EnableEurekaClient</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GatewayApplication</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        SpringApplication.run(GatewayApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>配置自动将注册中心的服务映射为路由</p><figure class="highlight yml"><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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">  port:</span> <span class="number">8081</span></span><br><span class="line"></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">  application:</span></span><br><span class="line"><span class="attr">    name:</span> <span class="string">gateway</span></span><br><span class="line"><span class="attr">  cloud:</span></span><br><span class="line"><span class="attr">    gateway:</span></span><br><span class="line">      <span class="comment"># 此处配置表示开启自动映射Eureka下发的路由</span></span><br><span class="line"><span class="attr">      discovery:</span></span><br><span class="line"><span class="attr">        locator:</span></span><br><span class="line"><span class="attr">          enabled:</span> <span class="literal">true</span></span><br><span class="line"><span class="attr">          lowerCaseServiceId:</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line"><span class="attr">  client:</span></span><br><span class="line">    <span class="comment"># Eureka Server地址</span></span><br><span class="line"><span class="attr">    service-url:</span></span><br><span class="line"><span class="attr">      defaultZone:</span> <span class="attr">http://localhost:8760/eureka/</span></span><br></pre></td></tr></table></figure></li><li><p>至此，已经可以直接通过gateway访问其他注册在Eureka中的服务的接口了。如客户端接口地址：<a href="http://localhost:8080/test，注册名称为client，则访问地址为http://localhost:8081/client/test。">http://localhost:8080/test，注册名称为client，则访问地址为http://localhost:8081/client/test。</a></p></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;简介：Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;项目搭建&quot;&gt;&lt;a href=&quot;#项目搭建&quot; class=&quot;headerlink&quot; ti
      
    
    </summary>
    
      <category term="SpringCloud" scheme="https://challange.github.io/categories/SpringCloud/"/>
    
    
      <category term="SpringCloud" scheme="https://challange.github.io/tags/SpringCloud/"/>
    
      <category term="Gateway" scheme="https://challange.github.io/tags/Gateway/"/>
    
  </entry>
  
  <entry>
    <title>从零开始玩转SpringCloud（一）：Eureka注册中心</title>
    <link href="https://challange.github.io/springcloud-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%8E%A9%E8%BD%ACSpringCloud%EF%BC%88%E4%B8%80%EF%BC%89%EF%BC%9AEureka%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/"/>
    <id>https://challange.github.io/springcloud-从零开始玩转SpringCloud（一）：Eureka注册中心/</id>
    <published>2019-04-07T06:50:20.000Z</published>
    <updated>2019-04-07T08:53:50.341Z</updated>
    
    <content type="html"><![CDATA[<h3 id="Eureka"><a href="#Eureka" class="headerlink" title="Eureka"></a>Eureka</h3><hr><blockquote><p>介绍：Eureka，古希腊词语，含义为我找到了，我发现了！相传阿基米德发现福利原理时说出了这个词。</p></blockquote><p>Eureka是Spring Cloud Netflix微服务套件中的一部分，可以与Springboot构建的微服务很容易的整合起来。Eureka包含了服务器端和客户端组件。服务器端，也被称作是服务注册中心，用于提供服务的注册与发现。Eureka支持高可用的配置，当集群中有分片出现故障时，Eureka就会转入自动保护模式，它允许分片故障期间继续提供服务的发现和注册，当故障分片恢复正常时，集群中其他分片会把他们的状态再次同步回来。客户端组件包含服务消费者与服务生产者。在应用程序运行时，Eureka客户端向注册中心注册自身提供的服务并周期性的发送心跳来更新它的服务租约。同时也可以从服务端查询当前注册的服务信息并把他们缓存到本地并周期性的刷新服务状态。</p><h4 id="Eureka-Server服务搭建"><a href="#Eureka-Server服务搭建" class="headerlink" title="Eureka-Server服务搭建"></a>Eureka-Server服务搭建</h4><hr><ol><li>引入依赖</li></ol><figure class="highlight xml"><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="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-eureka-server<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><ol start="2"><li>在Application中使用@EnableEurekaServer</li></ol><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><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.eureka;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.SpringApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;</span><br><span class="line"></span><br><span class="line"><span class="meta">@EnableEurekaServer</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EurekaApplication</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        SpringApplication.run(EurekaApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>配置文件</li></ol><figure class="highlight yml"><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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">  application:</span></span><br><span class="line"><span class="attr">    name:</span> <span class="string">eureka-server</span> <span class="comment"># cAPP名称，在Eureka注册名称</span></span><br><span class="line"><span class="attr">  profiles:</span></span><br><span class="line"><span class="attr">    active:</span> <span class="string">peer</span> </span><br><span class="line"></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line"><span class="attr">  instance:</span></span><br><span class="line"><span class="attr">    hostname:</span> <span class="string">peer</span> <span class="comment"># 服务注册中心实例的主机名</span></span><br><span class="line"><span class="attr">  client:</span></span><br><span class="line"><span class="attr">    register-with-eureka:</span> <span class="literal">false</span>  <span class="comment"># 是否注册自己</span></span><br><span class="line"><span class="attr">    fetch-registry:</span> <span class="literal">false</span></span><br><span class="line"><span class="attr">    serviceUrl:</span></span><br><span class="line"><span class="attr">      defaultZone:</span> <span class="attr">http://$&#123;eureka.instance.hostname&#125;:$&#123;server.port&#125;/eureka/</span></span><br><span class="line"><span class="attr">  server:</span></span><br><span class="line"><span class="attr">    enableSelfPreservation:</span> <span class="literal">false</span> <span class="comment">#关闭自我保护机制</span></span><br><span class="line"></span><br><span class="line"><span class="attr">logging:</span></span><br><span class="line"><span class="attr">  level:</span></span><br><span class="line"><span class="attr">    com:</span></span><br><span class="line"><span class="attr">      netflix:</span></span><br><span class="line"><span class="attr">        eureka:</span> <span class="string">info</span></span><br><span class="line"><span class="attr">        discovery:</span> <span class="string">info</span></span><br><span class="line"></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">  port:</span> <span class="number">8760</span></span><br></pre></td></tr></table></figure><p>至此一个Eureka-Server就搭建好了。</p><h4 id="Eureka-Server服务高可用"><a href="#Eureka-Server服务高可用" class="headerlink" title="Eureka-Server服务高可用"></a>Eureka-Server服务高可用</h4><blockquote><p>说到高可用，就是要保证一个节点挂掉，不会影响整个系统的运行。解决办法就是多部署几个实例，搭建集群，那么一个实例节点挂掉，其他实例仍可提供服务。</p></blockquote><ol><li>新建三个配置文件application-peer1.yml、application-peer2.yml、application-peer3，</li></ol><figure class="highlight yml"><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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">  application:</span></span><br><span class="line"><span class="attr">    name:</span> <span class="string">eureka-server</span></span><br><span class="line"><span class="attr">  profiles:</span></span><br><span class="line"><span class="attr">    active:</span> <span class="string">peer1</span></span><br><span class="line"></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line"><span class="attr">  instance:</span></span><br><span class="line"><span class="attr">    hostname:</span> <span class="string">peer1</span> <span class="comment">#服务注册中心实例的主机名</span></span><br><span class="line"><span class="attr">  client:</span></span><br><span class="line"><span class="attr">    serviceUrl:</span>     <span class="comment"># 另外几个注册中心地址</span></span><br><span class="line"><span class="attr">      defaultZone:</span> <span class="attr">http://localhost:8762/eureka,</span> <span class="attr">http://localhost:8763/eureka</span>  </span><br><span class="line"><span class="attr">    server:</span></span><br><span class="line"><span class="attr">      enableSelfPreservation:</span> <span class="literal">false</span> <span class="comment">#关闭自我保护</span></span><br><span class="line"></span><br><span class="line"><span class="attr">logging:</span></span><br><span class="line"><span class="attr">  level:</span></span><br><span class="line"><span class="attr">    com:</span></span><br><span class="line"><span class="attr">      netflix:</span></span><br><span class="line"><span class="attr">        eureka:</span> <span class="string">info</span></span><br><span class="line"><span class="attr">        discovery:</span> <span class="string">info</span></span><br><span class="line"></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">  port:</span> <span class="number">8761</span></span><br></pre></td></tr></table></figure><hr><figure class="highlight yml"><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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">  application:</span></span><br><span class="line"><span class="attr">    name:</span> <span class="string">eureka-server</span></span><br><span class="line"><span class="attr">  profiles:</span></span><br><span class="line"><span class="attr">    active:</span> <span class="string">peer2</span></span><br><span class="line"></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line"><span class="attr">  instance:</span></span><br><span class="line"><span class="attr">    hostname:</span> <span class="string">peer2</span> <span class="comment">#服务注册中心实例的主机名</span></span><br><span class="line"><span class="attr">  client:</span></span><br><span class="line"><span class="attr">    serviceUrl:</span>  <span class="comment"># 另外几个注册中心地址</span></span><br><span class="line"><span class="attr">      defaultZone:</span> <span class="attr">http://localhost:8761/eureka,</span> <span class="attr">http://localhost:8763/eureka</span></span><br><span class="line"><span class="attr">  server:</span></span><br><span class="line"><span class="attr">    enableSelfPreservation:</span> <span class="literal">false</span> <span class="comment">#关闭自我保护</span></span><br><span class="line"></span><br><span class="line"><span class="attr">logging:</span></span><br><span class="line"><span class="attr">  level:</span></span><br><span class="line"><span class="attr">    com:</span></span><br><span class="line"><span class="attr">      netflix:</span></span><br><span class="line"><span class="attr">        eureka:</span> <span class="string">info</span></span><br><span class="line"><span class="attr">        discovery:</span> <span class="string">info</span></span><br><span class="line">        </span><br><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">  port:</span> <span class="number">8762</span></span><br></pre></td></tr></table></figure><hr><figure class="highlight yml"><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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">  application:</span></span><br><span class="line"><span class="attr">    name:</span> <span class="string">eureka-server</span></span><br><span class="line"><span class="attr">  profiles:</span></span><br><span class="line"><span class="attr">    active:</span> <span class="string">peer3</span></span><br><span class="line"></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line"><span class="attr">  instance:</span></span><br><span class="line"><span class="attr">    hostname:</span> <span class="string">peer3</span> <span class="comment">#服务注册中心实例的主机名</span></span><br><span class="line"><span class="attr">  client:</span></span><br><span class="line"><span class="attr">    serviceUrl:</span>  <span class="comment"># 另外几个注册中心地址</span></span><br><span class="line"><span class="attr">      defaultZone:</span> <span class="attr">http://localhost:8761/eureka,</span> <span class="attr">http://localhost:8762/eureka</span></span><br><span class="line"><span class="attr">  server:</span></span><br><span class="line"><span class="attr">    enableSelfPreservation:</span> <span class="literal">false</span> <span class="comment">#关闭自我保护</span></span><br><span class="line"></span><br><span class="line"><span class="attr">logging:</span></span><br><span class="line"><span class="attr">  level:</span></span><br><span class="line"><span class="attr">    com:</span></span><br><span class="line"><span class="attr">      netflix:</span></span><br><span class="line"><span class="attr">        eureka:</span> <span class="string">info</span></span><br><span class="line"><span class="attr">        discovery:</span> <span class="string">info</span></span><br><span class="line"></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">  port:</span> <span class="number">8763</span></span><br></pre></td></tr></table></figure><ol start="2"><li><p>idea下按配置文件启动多个项目，Active profiles指定启动配置文件。分别启动刚刚写好的三个配置文件即可。<br><img src="/images/从零开始玩转SpringCloud（一）：Eureka注册中心/1.png" alt="在这里插入图片描述"></p></li><li><p>生产环境下是否需要动态配置注册中心，目前的配置对生产环境动态配置非常不友好</p></li></ol><h4 id="客户端配置"><a href="#客户端配置" class="headerlink" title="客户端配置"></a>客户端配置</h4><ol><li>引入Eureka Client依赖</li></ol><figure class="highlight xml"><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="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-eureka-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><ol start="2"><li>在Application中使用@EnableEurekaServer</li></ol><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><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.eureka;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.SpringApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;</span><br><span class="line"></span><br><span class="line"><span class="meta">@EnableEurekaClient</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EurekaClientApplication</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        SpringApplication.run(EurekaClientApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>配置文件</li></ol><figure class="highlight yml"><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><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">  application:</span></span><br><span class="line"><span class="attr">    name:</span> <span class="string">gateway</span>  <span class="comment"># 在eureka-server注册名称</span></span><br><span class="line">  </span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line"><span class="attr">  client:</span></span><br><span class="line"><span class="attr">    service-url:</span></span><br><span class="line"><span class="attr">      defaultZone:</span> <span class="attr">http://localhost:8760/eureka/</span> </span><br><span class="line"><span class="comment">#      #高可用配置</span></span><br><span class="line"><span class="comment">#      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/,http://localhost:8763/eureka/ </span></span><br><span class="line">      </span><br><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">  port:</span> <span class="number">8081</span></span><br></pre></td></tr></table></figure><ol start="4"><li>至此已EurekaClient已经搭建成功</li></ol><p><img src="/images/从零开始玩转SpringCloud（一）：Eureka注册中心/2.png" alt="在这里插入图片描述"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;Eureka&quot;&gt;&lt;a href=&quot;#Eureka&quot; class=&quot;headerlink&quot; title=&quot;Eureka&quot;&gt;&lt;/a&gt;Eureka&lt;/h3&gt;&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;介绍：Eureka，古希腊词语，含义为我找到了，我发现了！相传阿基米德发
      
    
    </summary>
    
      <category term="SpringCloud" scheme="https://challange.github.io/categories/SpringCloud/"/>
    
    
      <category term="SpringCloud" scheme="https://challange.github.io/tags/SpringCloud/"/>
    
      <category term="Eureka" scheme="https://challange.github.io/tags/Eureka/"/>
    
  </entry>
  
  <entry>
    <title>Scratch3.0——作品保存</title>
    <link href="https://challange.github.io/scratch-Scratch3-0%E2%80%94%E2%80%94%E4%BD%9C%E5%93%81%E4%BF%9D%E5%AD%98/"/>
    <id>https://challange.github.io/scratch-Scratch3-0——作品保存/</id>
    <published>2019-04-06T08:46:31.000Z</published>
    <updated>2019-05-26T04:08:23.809Z</updated>
    
    <content type="html"><![CDATA[<h3 id="整体思路概述"><a href="#整体思路概述" class="headerlink" title="整体思路概述"></a>整体思路概述</h3><h3 id="作品结构分析"><a href="#作品结构分析" class="headerlink" title="作品结构分析"></a>作品结构分析</h3><h3 id="服务端设计"><a href="#服务端设计" class="headerlink" title="服务端设计"></a>服务端设计</h3><h3 id="前端设计"><a href="#前端设计" class="headerlink" title="前端设计"></a>前端设计</h3>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;整体思路概述&quot;&gt;&lt;a href=&quot;#整体思路概述&quot; class=&quot;headerlink&quot; title=&quot;整体思路概述&quot;&gt;&lt;/a&gt;整体思路概述&lt;/h3&gt;&lt;h3 id=&quot;作品结构分析&quot;&gt;&lt;a href=&quot;#作品结构分析&quot; class=&quot;headerlink&quot; titl
      
    
    </summary>
    
      <category term="Scratch" scheme="https://challange.github.io/categories/Scratch/"/>
    
    
      <category term="Scratch" scheme="https://challange.github.io/tags/Scratch/"/>
    
  </entry>
  
  <entry>
    <title>Java字节码分析i=i++的结果</title>
    <link href="https://challange.github.io/Java%E5%9F%BA%E7%A1%80-Java%E5%AD%97%E8%8A%82%E7%A0%81%E5%88%86%E6%9E%90i-i-%E7%9A%84%E7%BB%93%E6%9E%9C/"/>
    <id>https://challange.github.io/Java基础-Java字节码分析i-i-的结果/</id>
    <published>2019-02-16T08:46:31.000Z</published>
    <updated>2019-05-26T05:53:12.765Z</updated>
    
    <content type="html"><![CDATA[<h3 id="示例代码"><a href="#示例代码" class="headerlink" title="示例代码"></a>示例代码</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i =<span class="number">3</span>;</span><br><span class="line">    i=i++;</span><br><span class="line">    System.out.println(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>运行结果是3，不是4，接下来就探索一下原因</strong></p><h3 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h3><p><img src="/images/Java字节码分析i=i++的结果/1.png" alt="分析"></p><blockquote><p>关键还是对<strong>i++</strong>即<strong>  IINC 1 1</strong>的理解。i++是直接在局部变量表上做自加操作。而<strong>i=i+1</strong>是先从局部变量表拷贝i的值到操作栈，在操作栈执行i+1操作，最后将操作栈的运算结果写入局部变量表，从这个角度也体现了i++的运算效率更高。</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;示例代码&quot;&gt;&lt;a href=&quot;#示例代码&quot; class=&quot;headerlink&quot; title=&quot;示例代码&quot;&gt;&lt;/a&gt;示例代码&lt;/h3&gt;&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre
      
    
    </summary>
    
      <category term="Java基础" scheme="https://challange.github.io/categories/Java%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="Java基础" scheme="https://challange.github.io/tags/Java%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>蚂蚁金服分布式事务框架</title>
    <link href="https://challange.github.io/%E5%BE%AE%E6%9C%8D%E5%8A%A1-%E8%9A%82%E8%9A%81%E9%87%91%E6%9C%8D%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A1%86%E6%9E%B6/"/>
    <id>https://challange.github.io/微服务-蚂蚁金服分布式事务框架/</id>
    <published>2019-01-31T08:05:05.000Z</published>
    <updated>2019-05-26T05:52:43.750Z</updated>
    
    <content type="html"><![CDATA[<h1 id="分布式事务"><a href="#分布式事务" class="headerlink" title="分布式事务"></a>分布式事务</h1><h3 id="基本术语"><a href="#基本术语" class="headerlink" title="基本术语"></a>基本术语</h3><table><thead><tr><th style="text-align:left">术语</th><th style="text-align:right">描述</th></tr></thead><tbody><tr><td style="text-align:left">事务</td><td style="text-align:right">事务是指作为单个逻辑工作单元执行的一系列操作，要么完全执行，要么完全不执行。</td></tr><tr><td style="text-align:left">分布式事务</td><td style="text-align:right">事务的发起者、资源及资源管理器和事务协调者分别位于不同的分布式系统的不同节点之上。</td></tr><tr><td style="text-align:left">分支事务</td><td style="text-align:right">一个分布式事务可能包含多个数据库本地事务，在分布式事务框架下，分支事务可能是一个分库上执行的 SQL 语句，或是一个自定义模式服务的调用。</td></tr><tr><td style="text-align:left">发起方</td><td style="text-align:right">分布式事务的发起方负责启动分布式事务，通过调用参与者的服务，将参与者纳入到分布式事务当中，并决定整个分布式事务是提交还是回滚。一个分布式事务有且只能有一个发起方。</td></tr><tr><td style="text-align:left">参与者</td><td style="text-align:right">参与者提供分支事务服务。当一个参与者被发起方调用，则被纳入到该发起方启动的分布式事务中，成为该分布式事务的一个分支事务。一个分布式事务可以有多个参与者。</td></tr><tr><td style="text-align:left">事务管理器</td><td style="text-align:right">事务管理器是一个独立的服务，用于协调分布式事务，包括创建主事务记录、分支事务记录，并根据分布式事务的状态，调用参与者提交或回滚方法。</td></tr><tr><td style="text-align:left">主事务记录</td><td style="text-align:right">又叫 Activity 记录，是整个分布式事务的主体。其最核心的数据结构是事务号（TX_ID）和事务状态（STATE），它是在启动分布式事务时持久化写入数据库的，它的状态决定了这个分布式事务的状态。</td></tr><tr><td style="text-align:left">分支事务记录</td><td style="text-align:right">又叫 Action 记录，用于标识分支事务。它记录了该提供该分支事务的参与者的信息，其中包括参与者的唯一标识等。通过分支事务信息，事务管理器就可以对参与者进行提交或者回滚操作。</td></tr><tr><td style="text-align:left">最终一致</td><td style="text-align:right">事务处理过程中，会有短暂不一致的情况，但通过恢复系统，可以让事务的数据达到最终一致的目标。</td></tr></tbody></table><h3 id="解决问题"><a href="#解决问题" class="headerlink" title="解决问题"></a>解决问题</h3><p>分布式事务解决三大核心问题：<strong>跨数据库</strong>、<strong>跨服务</strong>以及<strong>混合分布式事务</strong>。</p><h4 id="跨数据库分布式事务"><a href="#跨数据库分布式事务" class="headerlink" title="跨数据库分布式事务"></a>跨数据库分布式事务</h4><p>当业务规模增大，单库单表无法满足业务需求时，自然就会出现分库分表的情况。但是，单机事务又不能保证分库后的事务属性，分布式事务几乎无法避免。分布式事务可以让应用轻松具备跨库分布式事务处理能力，像使用单机数据库事务一样，透明地使用分布式事务。</p><h4 id="跨服务的分布式事务"><a href="#跨服务的分布式事务" class="headerlink" title="跨服务的分布式事务"></a>跨服务的分布式事务</h4><p>在基于 SOA（Service-Oriented Architecture，面向服务架构）的分布式应用环境下，系统按照功能解耦，拆分成不同的微服务，一项业务往往会涉及多个服务之间的调用。因此，为了保障业务完整，需要保证各调用服务间的数据一致性。分布式事务同样支持跨服务的分布式事务，并且可以很方便的与 SOFABoot、Spring Cloud、Dubbo 等框架打通，实现业务链路级别的分布式事务。</p><h4 id="混合分布式事务"><a href="#混合分布式事务" class="headerlink" title="混合分布式事务"></a>混合分布式事务</h4><p>在一个大规模的分布式应用环境下，除了常见的微服务、数据库资源之外，还会涉及到消息队列、缓存等系统资源的使用。同时，依然需要保证这些资源间访问的数据的一致性。分布式事务支持在同一个分布式事务中引入数据库、服务、消息队列等不同类型的资源，并且支持混合接入模式。</p><h3 id="角色简介"><a href="#角色简介" class="headerlink" title="角色简介"></a>角色简介</h3><p>分布式事务主要涉及三个角色，发起方、参与者与事务管理器，具体描述如下：</p><ul><li>发起方：分布式事务的发起方负责启动分布式事务，通过调用参与者的服务，将参与者纳入到分布式事务当中，并决定整个分布式事务是提交还是回滚。一个分布式事务有且只能有一个发起方。</li><li>参与者：参与者提供分支事务服务。当一个参与者被发起方调用后，该参与者会被纳入到该发起方启动的分布式事务中，成为该分布式事务的一个分支事务。一个分布式事务可以有多个参与者。</li><li>事务管理器：事务管理器是一个独立的服务，用于协调分布式事务，包括创建主事务记录、分支事务记录，并根据分布式事务的状态，调用参与者提交或回滚方法。</li></ul><h3 id="事务执行状态说明"><a href="#事务执行状态说明" class="headerlink" title="事务执行状态说明"></a>事务执行状态说明</h3><p>分布式事务使用两阶段提交协议（Two-Phase Commit Protocol，简称 2PC）来保证事务执行的原子性。2PC 包含两个阶段：</p><p>第一阶段，也称准备阶段。由事务发起者向各参与者发送请求，询问参与者是否准备好执行事务。<br>第二阶段，也称提交阶段。在一阶段所有参与者都确认可以执行事务后，各参与者开始分别执行事务分支。所有分支都成功则事务数据提交成功，否则，所有分支都进行数据回滚操作。<br>事务在某一时间点可能处在以下状态之一：</p><ul><li>初始化：应用发起事务</li><li>进行中<ul><li>准备中：一阶段操作中</li><li>提交中：一阶段结束，正在二阶段的提交操作中</li><li>回滚中：一阶段结束，因为某些业务失败，正在二阶段的回滚操作中</li></ul></li><li>已结束<ul><li>已提交：事务结束，事务执行的数据变更已提交</li><li>已回滚：事务结束，事务执行的数据变更已回滚</li></ul></li><li>异常<ul><li>提交异常：一阶段结束，二阶段处理提交操作时出现异常</li><li>回滚异常：一阶段结束，二阶段处理回滚操作时出现异常</li><li>回查异常：一阶段结束，二阶段处理回查业务接口时出现异常</li></ul></li></ul><h3 id="分布式事务中的二阶段提交是什么？"><a href="#分布式事务中的二阶段提交是什么？" class="headerlink" title="分布式事务中的二阶段提交是什么？"></a>分布式事务中的二阶段提交是什么？</h3><blockquote><p>二阶段提交协议（Two-phase Commit Protocol，简称 2PC）是分布式事务的核心协议。在此协议中，一个事务管理器（Transaction Manager，简称 TM）协调 1 个或多个资源管理器（Resource Manager，简称 RM）的活动，所有资源管理器向事务管理器汇报自身活动状态，由事务管理器根据各资源管理器汇报的状态（完成准备或准备失败）来决定各资源管理器是“提交”事务还是进行“回滚”操作。</p></blockquote><p>二阶段提交的具体流程如下：</p><ol><li>应用程序向事务管理器提交请求，发起分布式事务；</li><li>在第一阶段，事务管理器联络所有资源管理器，通知它们准备提交事务；</li><li>各资源管理器返回完成准备（或准备失败）的消息给事务管理器（响应超时算作失败）；</li><li>在第二阶段：<ul><li>如果所有资源管理器均完成准备（如图 1），则事务管理器会通知所有资源管理器执行事务提交；</li><li>如果任一资源管理器准备失败（如图 2 中的资源管理器 B），则事务管理器会通知所有资源管理器进行事务回滚。</li></ul></li></ol><p><img src="/images/蚂蚁金服分布式事务框架/1.png" alt="二阶段提交示意图"></p><h3 id="分布式事务中的-TCC-模型"><a href="#分布式事务中的-TCC-模型" class="headerlink" title="分布式事务中的 TCC 模型"></a>分布式事务中的 TCC 模型</h3><p>Try-Confirm-Cancel（TCC）是初步操作（Try）、确认操作（Confirm）和取消操作（Cancel）三种操作的缩写，这三种操作的业务含义如下：</p><ol><li>Try 阶段：对业务系统做检测及资源预留；</li><li>Confirm 阶段：对业务系统做确认提交。默认 Confirm 阶段是不会出错的，只要 Try 成功，Confirm 一定成功；</li><li>Cancel 阶段：当业务执行出现错误，需要回滚的状态下，执行业务取消，释放预留资源。</li></ol><p>TCC 是二阶段提交协议（Two-phase Commit Protocol，简称 2PC）的扩展，Try 操作对应 2PC 中一阶段的准备提交事务（Prepare），Confirm 对应 2PC 中二阶段事务提交（Commit），Cancel 对应 2PC 中二阶段事务回滚（Rollback）。</p><p>与 2PC 不同的是，TCC 是一种编程模型，是应用层的 2PC；TCC 的 3 个操作均由编码实现，通过编码实现了 2PC 资源管理器的功能。</p><p>TCC 自编码的特性决定 TCC 资源管理器可以跨数据库、跨应用实现资源管理，将对不同的数据库访问、不同的业务操作通过编码方式转换一个原子操作，解决了复杂业务场景下的事务问题。同时 TCC 的每一个操作对于数据库来讲都是一个本地数据库事务，操作结束则本地数据库事务结束，数据库的资源也就被释放；这就规避了数据库层面的 2PC 对资源占用导致的性能低下问题。</p><h3 id="柔性事务的定义与分类"><a href="#柔性事务的定义与分类" class="headerlink" title="柔性事务的定义与分类"></a>柔性事务的定义与分类</h3><h4 id="柔性事务的定义"><a href="#柔性事务的定义" class="headerlink" title="柔性事务的定义"></a>柔性事务的定义</h4><p><strong>刚性事务</strong>（如单数据库）完全遵循 ACID 规范，即数据库事务正确执行的四个基本要素：</p><ul><li>原子性（Atomicity）</li><li>一致性（Consistency）</li><li>隔离性（Isolation）</li><li>持久性（Durability）</li></ul><p><strong>柔性事务</strong>（如分布式事务）为了满足可用性、性能与降级服务的需要，降低一致性（Consistency）与隔离性（Isolation）的要求，遵循 BASE 理论：</p><ul><li>基本业务可用性（Basic Availability）</li><li>柔性状态（Soft state）</li><li>最终一致性（Eventual consistency）</li></ul><p>同样的，柔性事务也部分遵循 ACID 规范：<br>原子性：严格遵循<br>一致性：事务完成后的一致性严格遵循；事务中的一致性可适当放宽<br>隔离性：并行事务间不可影响；事务中间结果可见性允许安全放宽<br>持久性：严格遵循</p><h4 id="柔性事务的分类"><a href="#柔性事务的分类" class="headerlink" title="柔性事务的分类"></a>柔性事务的分类</h4><p>柔性事务分为：两阶段型、补偿型、异步确保型、最大努力通知型。</p><ol><li>两阶段型<br>分布式事务二阶段提交，对应技术上的 XA、JTA/JTS，这是分布式环境下事务处理的典型模式。</li><li>补偿型<br>TCC 型事务（Try-Confirm-Cancel）可以归为补偿型。在 Try 成功的情况下，如果事务要回滚，Cancel 将作为一个补偿机制，回滚 Try 操作；TCC 各操作事务本地化，且尽早提交（没有两阶段约束）；当全局事务要求回滚时，通过另一个本地事务实现“补偿”行为。<br>TCC 是将资源层的二阶段提交协议转换到业务层，成为业务模型中的一部分。</li><li>异步确保型<br>将一些有同步冲突的事务操作变为异步操作，避免对数据库事务的争用，如消息事务机制。</li><li>最大努力通知型<br>通过通知服务器（消息通知）进行，允许失败，有补充机制。</li></ol><h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><ol><li>有没有可能在回滚时已经时过境迁，回滚前该记录已经发生了很多次改变，那么如何应对？<br>在update之前，会先把账户的金额保存下来，执行update操作，然后把执行之后的金额保存下来。因为在二阶段有可能会是回滚操作，回滚的时候如果想把执行之前的数据覆盖回去的话，必须要保证在覆盖的那个时刻，这些行上面的数据没有被别人变更过，所以最后会加一个逻辑行锁，这个就是金融系统的特性需求。</li></ol><h3 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h3><p><a href="http://wwyz-study.oss-cn-hangzhou.aliyuncs.com/XTS%20%E6%94%AF%E4%BB%98%E5%AE%9D%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97.pdf">XTS支付宝分布式事务学习指南</a><br><a href="http://wwyz-study.oss-cn-hangzhou.aliyuncs.com/%E5%A4%A7%E8%A7%84%E6%A8%A1SOA%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E5%A4%84%E7%90%86_%E7%A8%8B%E7%AB%8B_SD2C2008.pdf">大规模SOA系统中的分布式事务处理_程立_SD2C2008</a><br><a href="https://www.zhihu.com/question/31813039">支付宝运营架构中柔性事务指的是什么？</a><br><a href="http://tech.it168.com/a2018/0523/3204/000003204767.shtml">分布式事务：蚂蚁金服核心金融场景下的演进</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;分布式事务&quot;&gt;&lt;a href=&quot;#分布式事务&quot; class=&quot;headerlink&quot; title=&quot;分布式事务&quot;&gt;&lt;/a&gt;分布式事务&lt;/h1&gt;&lt;h3 id=&quot;基本术语&quot;&gt;&lt;a href=&quot;#基本术语&quot; class=&quot;headerlink&quot; title=&quot;基本术语&quot;
      
    
    </summary>
    
      <category term="微服务" scheme="https://challange.github.io/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
    
      <category term="微服务" scheme="https://challange.github.io/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="分布式事务" scheme="https://challange.github.io/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/"/>
    
  </entry>
  
  <entry>
    <title>SQL调优的基本公式(转)</title>
    <link href="https://challange.github.io/%E6%95%B0%E6%8D%AE%E5%BA%93-SQL%E8%B0%83%E4%BC%98%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%85%AC%E5%BC%8F/"/>
    <id>https://challange.github.io/数据库-SQL调优的基本公式/</id>
    <published>2019-01-29T08:05:05.000Z</published>
    <updated>2019-05-26T05:54:21.366Z</updated>
    
    <content type="html"><![CDATA[<p>SQL的优化是DBA日常工作中不可缺少的一部分，记得在学生时期，曾经在ITPUB上看到一篇帖子，当时楼主在介绍SQL优化的时候，用一个公式来讲解他在做sql优化的时候遵循的原则：</p><blockquote><p>T=S/V(T代表时间，S代表路程，V代表速度)</p></blockquote><p>S指SQL所需访问的资源总量，V指SQL单位时间所能访问的资源量，T自然就是SQL执行所需时间了;我们为了获得SQL最快的执行时间，可以根据公式定义上去反推：</p><ol><li>在S不变的情况下，我们可以提升V来降低T：通过适当的索引调整，我们可以将大量的速度较慢的随机IO转换为速度较快的顺序IO；通过提升服务器的内存，使得将更多的数据放到内存中，会比数据放到磁盘上会得到明显的速度提升；采用电子存储介质进行数据存储和读取的SSD，突破了传统机械硬盘的性能瓶颈，使其拥有极高的存储性能;在提升V上我们可以采用较高配置的硬件来完成速度的提升；</li><li>在V不变的情况下，我们可以减小S来降低T：这是SQL优化中非常核心的一个环节，在减小S环节上，DBA可以做的可以有很多，通常可以在查询条件中建立适当的索引，来避免全表扫描；有时候可以改写SQl，添加一些适当的提示符，来改变SQL的执行计划，使SQL以最少的扫描路径完成查询；当这些方法都使用完了之后，你是否还有其他方案来优化喃？在阿里系的DBA职位描述中有条就是要求DBA需要深入的了解业务，当DBA深入的了解业务之后，这个时候能站在业务上，又站DB角度上考虑，这个时候在去做优化，有时候能达到事半功倍的效果。</li></ol><h3 id="案例一：通过降低S，来提升T"><a href="#案例一：通过降低S，来提升T" class="headerlink" title="案例一：通过降低S，来提升T"></a>案例一：通过降低S，来提升T</h3><blockquote><p>原理介绍：<br>我们知道B+索引叶子节点的值是按照索引字段升序的，比如我们对（nick，appkey）两个字段做了索引，那么在索引中的则是按照nick，appkey的升序排列；如果我们现在的一条sql：<br>select count(distinct nick) from xxxx_nickapp_09_29;<br>用于查询统计某天日志表中的UV，优化器选择了该表上索引ind_nick_appkey（nick，appkey）来完成查询，则开始从nick1开始一条条扫描下来，直到扫描到最后一个nick_n,那么中间过程会扫描很多重复的nick（最左边普通扫描），如果我们能够跳过中间重复的nick，则性能会优化非常多（最右边的松散扫描）：</p></blockquote><p>从上面的可以得到一个结论：</p><p>如果这条统计uv的sql能够按照右边的loose index scan的方式来扫描话，会大大的减小我们上面提到的S；所以需要通过改写sql来达到伪loose index scan：（MySql优化器不能直接的对count(distinct column)做优化）</p><p>root@DB 09:41:30&gt;select count(*) from ( select distinct(nick) from xxxx_nickapp_09_29) t ;</p><p>+———-+<br>| count(*) |<br>+———-+<br>| 806934 |<br>+———-+<br>Sql内查询中先选出不同的nick，最后在外面套一层count，就可以得到nick的distinct值总和；<br>最重要的是在子查询中：select distinct(nick) 实现了上图中的伪loose index scan，优化器在这个时候的执行计划为Using index for group-by ，这样mysql就把distinct优化为group by，首先利用索引来分组，然后扫描索引，对需要的nick只扫描一条记录。</p><p>真实案例：</p><p>该案例选自我们的一个线上的生产系统，该系统每天有大量的日志数据入库，单表的容量在10G-50G之间，然后做汇总分析，计算日志数据中的uv就是其中一个逻辑，sql如下：<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="keyword">count</span>(<span class="keyword">distinct</span> nick) <span class="keyword">from</span> xxxx_nickapp_09_29;</span><br></pre></td></tr></table></figure></p><p>即使在_xxxx分表上加上nick的索引，通过查看执行计划，为全索引扫描，由于单表的数据量过大，sql在执行的时候，会对整个服务器带来抖动，需要对原来的SQL进行改写，使其支持loose index scan；</p><p>优化前：</p><p>root@DB 09:41:30&gt;select count(distinct nick) from xxxx_nickapp_09_29;<br>+———-+<br>| count(*) |<br>+———-+<br>| 806934 |</p><p>1 row in set (52.78 sec)<br>执行一次sql需要花费52.78s</p><p>优化后：</p><p>root@DB 09:41:30&gt;select count(*) from ( select distinct(nick) from xxxx_nickapp_09_29)t ;</p><p>+———-+<br>| count(*) |<br>+———-+<br>| 806934 |<br>+———-+<br>1 row in set (5.81 sec)</p><p>由52.78秒降至5.81秒，速度提升了差不多10倍；</p><p>查看SQL的执行计划：</p><p>优化写法：</p><p>root@DB 09:41:30&gt;explain select count(*) from ( select distinct(nick) from xxxx_nickapp_09_29)t ;<br>| id      |    select_type | table  |type |possible_keys|key|key_len|ref|rows|Extra|<br>| :——– | ——–:| ——–:| ——–:| ——–:| ——–:| ——–:| ——–:| ——–:|:–: |<br>| 1  | SIMPLE |  xxxx_nickapp_09_29  |range|NULL|ind_nick_appkey|67|NULL|2124695|Using index for group-by|<br>原始写法：<br>root@DB 09:41:50&gt;explain select count(distinct nick) from xxxx_nickapp_09_29;<br>| id      |    select_type | table  |type |possible_keys|key|key_len|ref|rows|Extra|<br>| :——– | ——–:| ——–:| ——–:| ——–:| ——–:| ——–:| ——–:| ——–:|:–: |<br>| 1  | SIMPLE |  xxxx_nickapp_09_29  |index|NULL|ind_nick_appkey|177|NULL|19546123|Using index|</p><p>可以看到我们的路程由19546123减小到2124695，减小了9倍多.</p><h3 id="案例二：结合业务递增的写入特点，巧妙优化UV统计count"><a href="#案例二：结合业务递增的写入特点，巧妙优化UV统计count" class="headerlink" title="案例二：结合业务递增的写入特点，巧妙优化UV统计count(*)"></a>案例二：结合业务递增的写入特点，巧妙优化UV统计count(*)</h3><blockquote><p>有时候觉得，优化一条sql的最高境界就是让这sql能够从把这条从系统中拿掉，不管怎样，这些都是建立在你足够的了解业务上，就能够推动一些业务产品的升级或者下线，这样的DBA你能做到吗？</p></blockquote><p>下面看一个案例：<br>应用每天都会对入库的分表统计一个总数：select count(<em>) from xx_01_01;<br>随着单表的数据量越来越大（单表在20G左右），每次进行count的时候，速度越来越慢，同时需要扫描较多的数据页块，导致整个数据库性能的抖动，通过分析业务的特点，由于每张表采用自增id的方式进行插入，并且没有数据的删除，所以统计全表的总数就可以变通一下：<br>所以这条sql：select count(</em>) from xx_01_01;<br>可以变通为： select max(id)-min(id)+1 from xx_01_01;<br>执行速度可以得到质的飞跃.</p><h3 id="案例三：通过提升V，来降低T—随机IO-VS-顺序IO"><a href="#案例三：通过提升V，来降低T—随机IO-VS-顺序IO" class="headerlink" title="案例三：通过提升V，来降低T—随机IO  VS  顺序IO"></a>案例三：通过提升V，来降低T—随机IO  VS  顺序IO</h3><p>真实线上案例：在我们的一个核心产品库上，承载着非常大量的随机读，就叫它读库好了。一天读库的load非常的高，通过慢日志发现，有一条sql频繁的出现在慢日中，这条sql的查询条件很复杂，同时该表上的类似相同的索引也非常的多，当时是怀疑索引走错，通过explain 来查看SQL的执行计划：发现执行计划中的using where代表查询回表了，同时由于回表的记录rows较大，所以带来了大量的随机IO：</p><p>所以我们只需要在原来的索引冗余掉is_detail字段就可以通过覆盖索引的方法优化掉该sql，避免了查询回表而导致的随机io，用顺序io替换了原来的随机io，SQL的执行速度得到极大提升：（下图会去掉is_detail字段的测试）</p><p>总结：SQL优化是很有趣的一件事情，我们在日常工作中可以按照t=s/v的思路来进行优化，也许你第一次运用它的时候有些陌生，但是只要不断的练习，善于总结，你也会发现其中的规律，真是妙哉妙哉。还有一点很重要的是，你的SQL优化不要脱离实际业务，也许你在哪里优化一条sql花了1个小时，但是去和开发同学讨论优化成果的时候，开发同学说这条sql其实可以下线了，那时候真的哭笑不得了 .</p><p>原文：玄惭<a href="http://hidba.org/?p=498">http://hidba.org/?p=498</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;SQL的优化是DBA日常工作中不可缺少的一部分，记得在学生时期，曾经在ITPUB上看到一篇帖子，当时楼主在介绍SQL优化的时候，用一个公式来讲解他在做sql优化的时候遵循的原则：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;T=S/V(T代表时间，S代表路程，V代表速度)&lt;/p
      
    
    </summary>
    
      <category term="数据库" scheme="https://challange.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
      <category term="数据库" scheme="https://challange.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
      <category term="SQL调优" scheme="https://challange.github.io/tags/SQL%E8%B0%83%E4%BC%98/"/>
    
  </entry>
  
  <entry>
    <title>毕玄：我在阿里的十年技术感悟(转)</title>
    <link href="https://challange.github.io/%E7%A8%8B%E5%BA%8F%E4%BA%BA%E7%94%9F-%E6%AF%95%E7%8E%84%EF%BC%9A%E6%88%91%E5%9C%A8%E9%98%BF%E9%87%8C%E7%9A%84%E5%8D%81%E5%B9%B4%E6%8A%80%E6%9C%AF%E6%84%9F%E6%82%9F/"/>
    <id>https://challange.github.io/程序人生-毕玄：我在阿里的十年技术感悟/</id>
    <published>2019-01-29T08:05:05.000Z</published>
    <updated>2019-05-26T05:56:41.317Z</updated>
    
    <content type="html"><![CDATA[<h3 id="从业余程序员到职业程序员"><a href="#从业余程序员到职业程序员" class="headerlink" title="从业余程序员到职业程序员"></a>从业余程序员到职业程序员</h3><p>程序员刚入行时，我觉得最重要的是把自己培养成职业的程序员，我的程序员起步比同龄人都晚了很多，更不用说现在的年轻人了，我大学读的是生物专业，在上大学前基本算是完全没接触过计算机，军训的时候因为很无聊，我和室友每天跑去学校的机房玩，我现在还印象很深刻，我第一次走进机房的时候，别人问，你是要玩windows，还是dos，我那是完全的一抹黑，后来就只记得在机房一堆人都是在练习盲打，军训完，盲打倒是练的差不多了，对计算机就这么产生了浓厚的兴趣，大一的时候都是玩组装机，捣鼓了一些，对计算机的硬件有了那么一些了解。</p><p>到大二后，买了一些书开始学习当时最火的网页三剑客，学会了手写HTML、PS的基本玩法之类的，课余、暑假也能开始给人做做网站什么的(ps: 那个时候做网站真的好赚钱)，可能那样过了个一年左右，做静态的网页就不好赚钱了，也不好找实习工作，于是就开始学asp，写些简单的CRUD，做做留言板、论坛这些动态程序，应该算是在这个阶段接触编程了。</p><p>毕业后加入了深圳的一家做政府行业软件的公司，一个非常靠谱和给我空间的Leader，使得自己在那几年有了不错的成长，终于成了一个职业的程序员，通常来说，业余或半职业的程序员，多数是1个人，或者很小的一个团队一起开发，使得在开发流程、协作工具（例如jira、cvs/svn/git等）、测试上通常会有很大的欠缺，而职业的程序员在这方面则会专业很多，另外，通常，职业的程序员做的系统都要运行较长的时间，所以在可维护性上会特别注意，这点我是在加入阿里后理解更深的，一个运行10年的系统，和一个写来玩玩的系统显然是有非常大差别的。</p><p>这块自己感觉也很难讲清楚，只能说模模糊糊有个这样的概念，通常在有兴趣的基础上，从业余程序员跨越到成为职业程序员我觉得不会太难。</p><h3 id="编程能力的成长"><a href="#编程能力的成长" class="headerlink" title="编程能力的成长"></a>编程能力的成长</h3><p>作为程序员，最重要的能力始终是编程能力，就我自己的感受而言，我觉得编程能力的成长主要有这么几个部分。</p><h4 id="编程能力初级：会用"><a href="#编程能力初级：会用" class="headerlink" title="编程能力初级：会用"></a>编程能力初级：会用</h4><p>编程，首先都是从学习编程语言的基本知识学起的，不论是什么编程语言，有很多共同的基本知识，例如怎么写第一个Hello World、if/while/for、变量等，因此我比较建议在刚刚开始学一门编程语言的时候，还是就看看编程语言自己的一些文档就好，而不要上来就去看一些高阶的书，我当年学Java的时候上来就看Think in Java、Effective Java之类的，真心好难懂。</p><p>除了看文档以外，编程是个超级实践的活，所以一定要多写代码，只有这样才能真正熟练起来，这也是为什么我还是觉得在面试的时候让面试者手写代码是很重要的，这个过程是非常容易判断写代码的熟悉程度的，很多人会说由于写代码都是高度依赖IDE的，导致手写很难，但我绝对相信写代码写了很多的人，手写一段不是太复杂的可运行的代码是不难的，即使像我这种三年多没写过代码的人，让我现在手写一段不太复杂的可运行的Java程序，还是没问题的，前面N年的写代码生涯使得很多东西已经深入骨髓了。</p><p>我觉得编程能力初级这个阶段对于大部分程序员来说都不会是问题，勤学苦练，是这个阶段的核心。</p><h4 id="编程能力中级：会查和避免问题"><a href="#编程能力中级：会查和避免问题" class="headerlink" title="编程能力中级：会查和避免问题"></a>编程能力中级：会查和避免问题</h4><p>除了初级要掌握的会熟练的使用编程语言去解决问题外，中级我觉得首先是提升查问题的能力。</p><p>在写代码的过程中，出问题是非常正常的，<strong>怎么去有效且高效的排查问题</strong>，是程序员群体中通常能感受到的大家在编程能力上最大的差距，解决问题能力强的基本很容易在程序员群体里得到很高的认可，在查问题的能力上，首先要掌握的是一些基本的调试技巧，好用的调试工具，就像在Java里JDK自带的jstat、jmap、jinfo，不在JDK里的mat、gperf、btrace等，工欲善其事必先利其器，在查问题上是非常典型的，有些时候大家在查问题时的能力差距，有可能仅仅是因为别人比你多知道一个工具而已，除了调试技巧和工具外，查问题的更高境界会和编程能力的高级阶段有非常大的关系，就是<strong>懂原理</strong>，一个懂原理的程序员在查问题的水平上是有明显差距的，我想很多的同学应该能感受到，有些时候查出问题的原因仅仅是因为有效的工具，知其然不知其所以然，我给很多阿里的同学培训过Java排查问题的方法，在这个培训里，我经常也会讲到查问题的能力的培养最主要的也是熟练，多尝试给自己写一些会出问题的程序，多积极的看别人是怎么查问题的，多积极的去参与排查问题，很多最后查问题能力强的人多数仅仅是因为“无他，但手熟尔”。</p><p>就像我自己，排查问题能力的提升主要是在2009年和2010年，那两年作为淘宝消防队（处理各种问题和故障的虚拟团队）的成员处理了很多的故障和问题，当时消防队还有阿里最公认的技术大神多隆，向他学习到了很多排查问题的技巧，和他比，我排查问题的能力就是初级的那种，我印象最深刻的是有一次我们一起查一个应用cpu us高的问题，我们两定位到是一段代码在某种输入参数的时候会造成cpu us高的原因后，我能想到的继续查的方法是去生产环境抓输入参数，然后再用参数来本地debug看是什么原因，但多隆在看了一会那段代码后，给了我一个输入参数，我拿这个参数一运行，果然cpu us很高，哎，而且这种case不是一次两次，所以我经常和别人说，我是需要有问题场景才能排查出问题的，但多隆是完全有可能直接看代码就能看出问题的，这是本质的差距。</p><p>除了查问题外，更厉害的程序员是在写代码的过程就会很好的去<strong>避免问题</strong>，大家最容易理解的就是在写代码时处理各种异常情况，但这里通常也是程序员们很大的差距的地方，写一段正向逻辑的代码，大部分情况下即使有差距，也不会太大，<strong>但在怎么很好的处理这个过程中有可能出现的异常上，这个时候的功力差距会非常明显</strong>，很多时候一段代码里处理异常逻辑的部分都会超过正常逻辑的代码量，我经常说，一个优秀程序员和普通程序员的差距，很多时候压根就不需要看什么满天飞的架构图，而只用show一小段的代码就可以，举一个小case大家感受下，当年有一个严重故障，最后查出的原因是输入的参数里有一个是数组，把这个数组里的值作为参数去查数据库，结果前面输入了一个很大的数组，导致从数据库查了大量的数据，内存溢出了，很多程序员现在看都会明白对入参、出参的保护check，但类似这样的case在我自己排查问题的经历了真的碰到了好多。</p><p>在中级这个阶段，我会推荐大家尽可能的多刻意的去培养下自己这两个方面的能力，成为一个能写出高质量代码、有效排查问题的优秀程序员。</p><h4 id="编程能力高级：懂高级API和原理"><a href="#编程能力高级：懂高级API和原理" class="headerlink" title="编程能力高级：懂高级API和原理"></a>编程能力高级：懂高级API和原理</h4><p>就我自己的经历而言，我是在写了多年的Java代码后，才开始真正更细致的学习和掌握Java的一些更高级的API，我相信多数Java程序员也是如此，我算是从2003年开始用Java写商业系统的代码，但直到在2007年加入淘宝后，才开始非常认真的学习Java的IO通信、并发这些部分的API，尽管以前也学过也写过一些这样的代码，但完全就是皮毛，当然，这些通常来说有很大部分的原因会是工作的相关性，多数的写业务系统的程序员可能基本就不需要用到这些，所以导致会很难懂这些相对高级一些的API，但这些API对真正的理解一门编程语言我觉得至关重要，在之前的程序员成长路线的文章里我也讲到了这个部分，在没有场景的情况下，只能靠自己去创造场景来学习好，我觉得只要有足够的兴趣，这个问题还是不大的，毕竟现在有各种开源，这些是可以非常好的帮助自己创造机会学习的，例如学Java NIO，可以自己基于NIO包一个框架，然后对比Netty，看看哪些写的是不如Netty的，这样会非常有助于真正的理解。</p><p>在学习高级API的过程中，以及排查问题的过程中，我自己越来越明白懂编程语言的运行原理是非常重要的，因此我到了后面的阶段开始学习Java的编译机制、内存管理、线程机制等，对于我这种非科班出身的而言，学这些会因为缺乏基础更难很多，但这些更原理性的东西学会了后，对自己的编程能力会有质的提升，包括以后学习其他编程语言的能力，学这些原理最好的方法我觉得是先看看一些讲相关知识的书，然后去翻看源码，这样才能真正的更好的掌握，最后是在以后写代码的过程中、查问题的过程中多结合掌握的原理，才能做到即使在N年后也不会忘。</p><p>在编程能力的成长上，我觉得没什么捷径，非常赞同1万小时理论，在中级、高级阶段如果有人指点或和优秀的程序员们共事，会好非常多，不过我觉得这个和读书也有点像，到了一定阶段后（例如高中），天分会成为最重要的分水岭，不过就和大部分行业一样，大部分的情况下都还没到拼天分的时候，只需要拼勤奋就好。</p><h4 id="系统设计能力的成长"><a href="#系统设计能力的成长" class="headerlink" title="系统设计能力的成长"></a>系统设计能力的成长</h4><p>除了少数程序员会进入专深的领域，例如Linux Kernel、JVM，其他多数的程序员除了编程能力的成长外，也会越来越需要在系统设计能力上成长。</p><p>通常一个编程能力不错的程序员，在一定阶段后就会开始承担一个模块的工作，进而承担一个子系统、系统、跨多领域的更大系统等。</p><p>我自己在工作的第三年开始承担一个流程引擎的设计和实现工作，算是一个不算小的系统，并且也是当时那个项目里的核心部分，那个阶段学会了一些系统设计的基本知识，例如需要想清楚整个系统的目标、模块的划分和职责、关键的对象设计等，而不是上来就开始写代码，但那个时候由于我是一个人写整个系统，所以其实对设计的感觉并还没有那么强力的感觉。</p><p>第三段经历，是做阿里电商的异地多活，这对我来说是真正的去做一个巨大系统的架构师，尽管我以前做HSF的时候参与了淘宝电商2.0-3.0的重大技术改造，但参与和自己主导是有很大区别的，这个架构改造涉及到了阿里电商众多不同专业领域的技术团队，在这个阶段，我学会的最主要的：</p><p>1). 子系统职责划分，在这种超大的技术方案中，很容易出现某些部分的职责重叠和冲突，这个时候怎么去划分子系统，就非常重要了，作为大架构师，这个时候要从团队的职责、团队的可持续性上去选择团队；</p><p>2). 大架构师最主要的职责是控制系统风险，对于这种超大系统，一定是多个专业领域的架构师和大架构师共同设计，怎么确保在执行的过程中对于系统而言最重要的风险能够被控制住，这是我真正的理解什么叫系统设计文档里设计原则的部分，设计原则我自己觉得就是用来确保各个子系统在设计时都会遵循和考虑的，一定不能是虚的东西，例如在异地多活架构里，最重要的是如何控制数据风险，这个需要在原则里写上，最基本的原则是可接受系统不可用，但也要保障数据一致，而我看过更多的系统设计里设计原则只是写写的，或者千篇一律的，设计原则切实的体现了架构师对目标的理解（例如当时异地多活这个其实开始只是个概念，但做到什么程度才叫做到异地多活，这是需要解读的，也要确保在技术层面的设计上是达到了目标的），技术方案层面上的选择原则，并确保在细节的设计方案里有对于设计原则的承接以及执行；</p><p>3). 考虑问题的全面性，像异地多活这种大架构改造，涉及业务层面、各种基础技术层面、基础设施层面，对于执行节奏的决定要综合考虑人力投入、机器成本、基础设施布局诉求、稳定性控制等，这会比只是做一个小的系统的设计复杂非常多。</p><p>系统设计能力的成长，我自己觉得最重要的一是先在一两个技术领域做到专业，然后尽量扩大自己的知识广度，例如除了自己的代码部分外，还应该知道具体是怎么部署的，部署到哪去了，部署的环境具体是怎么样的，和整个系统的关系是什么样的，像我自己，是在加入基础设施团队后才更加明白有些时候软件上做的一个决策，会导致基础设施上巨大的硬件、网络或机房的投入，但其实有可能只需要在软件上做些调整就可以避免，做做研发、做做运维可能是比较好的把知识广度扩大的方法，第二点是练习自己做tradeoff的能力，这个比较难，做tradeoff这事需要综合各种因素做选择，但这也是所有的架构师最关键的，可以回头反思下自己在做各种系统设计时做出的tradeoff是什么，这个最好是亲身经历，听一些有经验的架构师分享他们选择背后的逻辑也会很有帮助，尤其是如果恰好你也在同样的挑战阶段，光听最终的架构结果其实大多数时候帮助有限。</p><p>技术Leader我觉得最好是能在架构师的基础上，后续注重成长的方面还是有挺大差别，就不在这篇里写了，后面再专门来写一篇。</p><h4 id="程序员金字塔"><a href="#程序员金字塔" class="headerlink" title="程序员金字塔"></a>程序员金字塔</h4><p>我认为程序员的价值关键体现在作品上，被打上作品标签是一种很大的荣幸，作品影响程度的大小我觉得决定了金字塔的层次，所以我会这么去理解程序员的金字塔。</p><p>在那之后的几年也负责过一些系统，但总体感觉好像在系统设计上的成长没那么多，直到在阿里的经历，才敢上自己在系统设计上有了越来越多的体会（References里有一篇我在系统设计上犯过的14个错，可以看到我走的一堆的弯路），在阿里有一次做分享，讲到我在系统设计能力方面的成长，主要是因为三段经历，负责专业领域系统的设计 -&gt; 负责跨专业领域的专业系统的设计 -&gt; 负责阿里电商系统架构级改造的设计。</p><p>第一段经历，是我负责HSF，HSF是一个从0开始打造的系统，它主要是作为支撑服务化的框架，是个非常专业领域的系统，放在整个淘宝电商的大系统来看，其实它就是一个很小的子系统，这段经历里让我最深刻的有三点：</p><p>1). 要设计好这种非常专业领域的系统，专业的知识深度是非常重要的，我在最早设计HSF的几个框的时候，是没有设计好服务消费者/提供者要怎么和现有框架结合的，在设计负载均衡这个部分也反复了几次，这个主要是因为自己当时对这个领域掌握不深的原因造成的;</p><p>2). 太技术化，在HSF的阶段，出于情怀，在有一个版本里投入了非常大的精力去引进OSGi以及去做动态化，这个后来事实证明是个非常非常错误的决定，从这个点我才真正明白在设计系统时一定要想清楚目标，而目标很重要的是和公司发展阶段结合；</p><p>3). 可持续性，作为一个要在生产环境持续运行很多年的系统而言，怎么样让其在未来更可持续的发展，这个对设计阶段来说至关重要，这里最low的例子是最早设计HSF协议的时候，协议头里竟然没有版本号，导致后来升级都特别复杂，最典型的例子是HSF在早期缺乏了缺乏了服务Tracing这方面的设计，导致后面发现了这个地方非常重要后，全部落地花了长达几年的时间，又例如HSF早期缺乏Filter Chain的设计，导致很多扩展、定制化做起来非常不方便。</p><p>第二段经历，是做T4，T4是基于LXC的阿里的容器，它和HSF的不同是，它其实是一个跨多领域的系统，包括了单机上的容器引擎，容器管理系统，容器管理系统对外提供API，其他系统或用户通过这个来管理容器，这个系统发展过程也是各种犯错，犯错的主要原因也是因为领域掌握不深，在做T4的日子里，学会到的最重要的是怎么去设计这种跨多个专业领域的系统，怎么更好的划分模块的职责，设计交互逻辑，这段经历对我自己更为重要的意义是我有了做更大一些系统的架构的信心。</p><p>当然，要打造一款作品，仅有上面的两点能力是不够的，作品里很重要的一点是对业务、技术趋势的判断，希望作为程序员的大伙，都能有机会打造一款世界级的作品，去为技术圈的发展做出贡献。</p><p>由于目前IT技术更新速度还是很快的，程序员这个行当是特别需要学习能力的，我一直认为，只有对程序员这个职业真正的充满兴趣，保持自驱，才有可能在这个职业上做好，否则的话是很容易淘汰的。</p><p>本文转自毕玄老师个人公众号：hellojavacases</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;从业余程序员到职业程序员&quot;&gt;&lt;a href=&quot;#从业余程序员到职业程序员&quot; class=&quot;headerlink&quot; title=&quot;从业余程序员到职业程序员&quot;&gt;&lt;/a&gt;从业余程序员到职业程序员&lt;/h3&gt;&lt;p&gt;程序员刚入行时，我觉得最重要的是把自己培养成职业的程序员，我
      
    
    </summary>
    
      <category term="程序人生" scheme="https://challange.github.io/categories/%E7%A8%8B%E5%BA%8F%E4%BA%BA%E7%94%9F/"/>
    
    
      <category term="程序人生" scheme="https://challange.github.io/tags/%E7%A8%8B%E5%BA%8F%E4%BA%BA%E7%94%9F/"/>
    
  </entry>
  
  <entry>
    <title>Scratch3.0——作品截图</title>
    <link href="https://challange.github.io/scratch-Scratch3-0%E2%80%94%E2%80%94%E4%BD%9C%E5%93%81%E6%88%AA%E5%9B%BE/"/>
    <id>https://challange.github.io/scratch-Scratch3-0——作品截图/</id>
    <published>2019-01-23T08:46:31.000Z</published>
    <updated>2019-05-26T06:06:02.399Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>Scratch 的舞台是基于canvas，最初尝试直接通过canvas的dom，然后生成图片，但最后只能得到一个黑色的图片，得到黑色图片的原因是没有取到有效的canvas而不是因为图片跨域，当初在这里走了很多弯路，继续研究舞台组件stage.jsx，从vm.renderer可以获取canvas，于是通过这个canvas对象生成图片，记得当时的效果是偶尔会得到有效图片，但是大部分时候依然是黑色的图片，原因稍后回解释。为了实现截图，当时又进一步研究了renderer的代码，最后找到了draw方法，通过多次尝试发现<strong>在draw方法的最后执行canvas对象生成图片可以获得舞台的有效图片</strong>。</p></blockquote><h4 id="最初的笨办法"><a href="#最初的笨办法" class="headerlink" title="最初的笨办法"></a>最初的笨办法</h4><p>在node_modules中找到scratch-render/src/RenderWebGL.js中的draw方法，也可以直接在dist中修改编译后的文件。顺便解释一下draw是对舞台进行了清理和重新绘制，而draw的频率非常频繁，因此不能直接通过canvas获取图片。在重绘后追加获取图片的toDataURL方法，考虑到需要在gui里面调用，此处采用了监听键盘事件来通信，接收到截图请求将舞台图片放在window.sessionStorage内存中，在需要使用的时候可以直接从sessionStorage获得。<br><figure class="highlight javascript"><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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">draw () &#123;</span><br><span class="line">       <span class="keyword">this</span>._doExitDrawRegion();</span><br><span class="line"><span class="comment">// 获取gl</span></span><br><span class="line">       <span class="keyword">const</span> gl = <span class="keyword">this</span>._gl;</span><br><span class="line"><span class="comment">// </span></span><br><span class="line">       twgl.bindFramebufferInfo(gl, <span class="literal">null</span>);</span><br><span class="line">       gl.viewport(<span class="number">0</span>, <span class="number">0</span>, gl.canvas.width, gl.canvas.height);</span><br><span class="line">       gl.clearColor.apply(gl, <span class="keyword">this</span>._backgroundColor);</span><br><span class="line">       gl.clear(gl.COLOR_BUFFER_BIT);</span><br><span class="line"><span class="comment">// 重新绘制</span></span><br><span class="line">       <span class="keyword">this</span>._drawThese(<span class="keyword">this</span>._drawList, ShaderManager.DRAW_MODE.default, <span class="keyword">this</span>._projection);</span><br><span class="line">       <span class="comment">// 增加如下代码</span></span><br><span class="line">       <span class="keyword">let</span> img = <span class="keyword">new</span> Image();</span><br><span class="line">       img.src = gl.canvas.toDataURL(<span class="string">'image/png'</span>,<span class="number">0.7</span>)</span><br><span class="line">       <span class="built_in">document</span>.onkeydown = <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">           <span class="keyword">if</span>(e.keyCode == <span class="number">16</span>)&#123;</span><br><span class="line">               <span class="built_in">window</span>.sessionStorage.setItem(<span class="string">"coverImg"</span>,img.src)</span><br><span class="line">               <span class="built_in">console</span>.log(<span class="string">'webGL'</span>)</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p><h4 id="带来问题"><a href="#带来问题" class="headerlink" title="带来问题"></a>带来问题</h4><ol><li>直接修改node_modules依赖的内容，严重影响团队开发、项目部署，提升了项目维护的复杂度。</li><li>每次draw都会执行toDataURL方法，并且赋值，增大了系统开销。</li><li>通过事件映射，提升了项目的复杂度。</li></ol><h3 id="优化"><a href="#优化" class="headerlink" title="优化"></a>优化</h3><p>回归最初问题的本源，不能直接从canvas.toDataURL获得舞台截图的原因是执行toDataURL的时候可能正好draw在重绘。因此先截图前先draw然后获取图片。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">this</span>.renderer.draw();</span><br><span class="line"><span class="keyword">const</span> img = <span class="keyword">new</span> Image();</span><br><span class="line">img.src = <span class="keyword">this</span>.canvas.toDataURL(<span class="string">'image/png'</span>, <span class="number">0.7</span>);</span><br></pre></td></tr></table></figure></p><p>码猿Scratch学习平台：<a href="https://scratch.imayuan.com">https://scratch.imayuan.com</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;Scratch 的舞台是基于canvas，最初尝试直接通过canvas的dom，然后生成图片，但最后只能得到一个黑色的图片，得到黑色图片的原因是没有取到有效的canvas而不是因为图片跨域，当初在这里走了很多弯路，继续研究舞台组件stage.jsx
      
    
    </summary>
    
      <category term="Scratch" scheme="https://challange.github.io/categories/Scratch/"/>
    
    
      <category term="Scratch" scheme="https://challange.github.io/tags/Scratch/"/>
    
  </entry>
  
  <entry>
    <title>Scratch3.0——克隆代码仓库的正确姿势</title>
    <link href="https://challange.github.io/scratch-Scratch3-0%E2%80%94%E2%80%94%E5%85%8B%E9%9A%86%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93%E7%9A%84%E6%AD%A3%E7%A1%AE%E5%A7%BF%E5%8A%BF/"/>
    <id>https://challange.github.io/scratch-Scratch3-0——克隆代码仓库的正确姿势/</id>
    <published>2019-01-23T08:46:31.000Z</published>
    <updated>2019-05-26T06:06:02.395Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>对Scratch3.0进行二次开发，首先要在github上fock<a href="https://github.com/LLK/scratch-gui.git">官方代码</a>，但是在自己开发的同时又要跟进官方的代码就要在git做如下配置。</p></blockquote><h4 id="步骤："><a href="#步骤：" class="headerlink" title="步骤："></a>步骤：</h4><p>1、配置上游项目地址。即将你 fork 的项目的地址给配置到自己的项目上。使用以下命令来配置。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ git remote add upstream https://github.com/LLK/scratch-gui.git</span><br></pre></td></tr></table></figure></p><p>然后可以查看一下配置状况，很好，上游项目的地址已经被加进来了。<br><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">➜ git remote -v</span><br><span class="line">origin https://github.com/***/scratch-gui.git (fetch)</span><br><span class="line">origin  https://github.com/***/scratch-gui.git (push)</span><br><span class="line">upstream        https://github.com/LLK/scratch-gui.git (fetch)</span><br><span class="line">upstream        https://github.com/LLK/scratch-gui.git (push)</span><br></pre></td></tr></table></figure></p><p>2、获取上游(官方)项目更新。使用 fetch 命令更新，fetch 后会被存储在一个本地分支 upstream/master 上。如果长时间没有更新，可能会非常慢，一定要在网络环境好的情况下更新或从GitHub下载代码。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ git fetch upstream</span><br></pre></td></tr></table></figure></p><p>3、合并到本地分支。切换到 master 分支，合并 upstream/master 分支。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ git merge upstream/master</span><br></pre></td></tr></table></figure></p><p>如果提示： fatal: refusing to merge unrelated histories，这是因为本地和远端已经是两个独立的版本库，git认为是不相干的版本库。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ git merge upstream/master --allow-unrelated-histories</span><br></pre></td></tr></table></figure></p><p>4、合并冲突。因为是在原先代码的基础上二次开发，冲突不可避免，而最费时间的也是这里解决冲突这一步。</p><p>5、提交推送。根据自己情况提交推送自己项目的代码。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ git push origin master</span><br></pre></td></tr></table></figure></p><p>由于项目已经配置了上游项目的地址，所以如果 fork 的项目再次更新，重复步骤 2、3、4即可。</p><h4 id="留几个问题供大家思考交流："><a href="#留几个问题供大家思考交流：" class="headerlink" title="留几个问题供大家思考交流："></a>留几个问题供大家思考交流：</h4><ol><li>冲突在所难免，在开发中注意什么可以更快更高效的解决冲突合并代码？</li><li>如果官方代码重构了某部分模块，你的代码严重依赖该模块该如何处理？</li></ol><blockquote><p>如果需要修改开源系统，不要改动原系统，而是要开发辅助系统。（——从零开始学架构）</p></blockquote><p>码猿Scratch学习平台：<a href="https://scratch.imayuan.com">https://scratch.imayuan.com</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;对Scratch3.0进行二次开发，首先要在github上fock&lt;a href=&quot;https://github.com/LLK/scratch-gui.git&quot;&gt;官方代码&lt;/a&gt;，但是在自己开发的同时又要跟进官方的代码就要在git做如下配置。&lt;/
      
    
    </summary>
    
      <category term="Scratch" scheme="https://challange.github.io/categories/Scratch/"/>
    
    
      <category term="Scratch" scheme="https://challange.github.io/tags/Scratch/"/>
    
  </entry>
  
</feed>
