Linux MySQL分庫分表之Mycat

介紹

背景

  • 表的個數達到了幾百千萬張表時,眾多的業務模塊都訪問這個數據庫,壓力會比較大,考慮對其進行分庫
  • 表的數據達到幾千萬級別,在做很多操作都比較吃力,考慮對其進行分庫或分表

數據切分(sharding)方案

  數據的切分(Sharding)根據其切分規則的類型,可以分為兩種切分模式:

  • 垂直切分:按照業務模塊進行切分,將不同模塊的表切分到不同的數據庫中
  • 水平切分,將一張大表按照一定的切分規則,按照行切分成不同的表或者切分到不同的庫中

如何理解垂直切分?

  垂直分庫:主要解決的問題是單個數據庫中[數據表]過多問題

  垂直分表:主要解決的問題是單個中[過多問題(將一張大表,拆分不同的關聯表)。

如何理解水平切分?

  水平切分主要解決的問題就是對於[單表數據量過大]的問題(1000W以上數據性能會有所下降)

切分原則

  1. 能不切盡量不要切分
  2. 如果要切分一定要選擇合適的切分規則,提前規劃好
  3. 數據切分盡量通過冗餘或表分組(Table Group)來降低跨庫Join的可能
  4. 由於數據庫中間件對數據Join實現的優劣難以把握,而且實現高性能難度極大,業務讀取盡量少使用多表Join

分庫分表之後帶來問題?

  1. 跨庫Join:訂單表需要關聯會員信息(訂單表和會員表拆分為兩個庫的表)
    1. 應用層由一個查詢拆分為多個
    2. 全局表,每個庫都存儲相同的數據,比如字典表、地址表
    3. 字段冗餘
    4. Mycat技術可以實現跨庫Join,只能實現2張表跨庫Join
  2. 分佈式事務(Mycat沒有很好實現分佈式事務)
    1. 強一致性(互聯網項目不推薦,性能不好)
    2. 最終一致性(異步方式去實現,需要通過日誌信息)
  3. 主鍵問題(保證ID的連續性和唯一性)
    1. UUID(性能不好)
    2. redis incr命令
    3. zookeeper
    4. 雪花算法
  4. 跨庫進行排序問題
    1. 在應用層進行排序

Mycat應用

官網鏈接

點我直達

Mycat核心概念

  • Schema:由它制定邏輯數據庫(相當於MySQL的database數據庫)
  • Table:邏輯表(相當於MySQL的table表)
  • DataNode:真正存儲數據的物理節點
  • DataHost:存儲節點所在的數據庫主機(指定MySQL數據庫的連接信息)
  • User:MyCat的用戶(類似於MySQL的用戶,支持多用戶)

MyCat主要解決的問題

  • 海量數據存儲
  • 查詢優化

Mycat對數據庫的支持

Mycat安裝

安裝要求

  • jdk:要求jdk必須是1.7及以上版本 (我使用的是jdk 1.8

  • Mysql:推薦mysql是5.5以上版本(我使用的是mysql 5.7

安裝jdk

具體教程:點我直達

Mcat下載

下載鏈接:點我直達

百度雲盤地址:https://pan.baidu.com/s/14A3BAwnBRGZppc3AicF5Hw  密碼: gkrp

解壓

修改配置文件

路徑:/cyb/soft/mycat/conf

server.xml

用途:用於配置用戶信息

<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License"); 
    - you may not use this file except in compliance with the License. - You 
    may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 
    - - Unless required by applicable law or agreed to in writing, software - 
    distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT 
    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the 
    License for the specific language governing permissions and - limitations 
    under the License. -->
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
    <system>
    <property name="useSqlStat">0</property>  <!-- 1為開啟實時統計、0為關閉 -->
    <property name="useGlobleTableCheck">0</property>  <!-- 1為開啟全加班一致性檢測、0為關閉 -->

        <property name="sequnceHandlerType">2</property>
      <!--  <property name="useCompression">1</property>--> <!--1為開啟mysql壓縮協議-->
        <!--  <property name="fakeMySQLVersion">5.6.20</property>--> <!--設置模擬的MySQL版本號-->
    <!-- <property name="processorBufferChunk">40960</property> -->
    <!-- 
    <property name="processors">1</property> 
    <property name="processorExecutor">32</property> 
     -->
        <!--默認為type 0: DirectByteBufferPool | type 1 ByteBufferArena-->
        <property name="processorBufferPoolType">0</property>
        <!--默認是65535 64K 用於sql解析時最大文本長度 -->
        <!--<property name="maxStringLiteralLength">65535</property>-->
        <!--<property name="sequnceHandlerType">0</property>-->
        <!--<property name="backSocketNoDelay">1</property>-->
        <!--<property name="frontSocketNoDelay">1</property>-->
        <!--<property name="processorExecutor">16</property>-->
        <!--
            <property name="serverPort">8066</property> <property name="managerPort">9066</property> 
            <property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property> 
            <property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
        <!--分佈式事務開關,0為不過濾分佈式事務,1為過濾分佈式事務(如果分佈式事務內只涉及全局表,則不過濾),2為不過濾分佈式事務,但是記錄分佈式事務日誌-->
        <property name="handleDistributedTransactions">0</property>
        
            <!--
            off heap for merge/order/group/limit      1開啟   0關閉
        -->
        <property name="useOffHeapForMerge">1</property>

        <!--
            單位為m
        -->
        <property name="memoryPageSize">1m</property>

        <!--
            單位為k
        -->
        <property name="spillsFileBufferSize">1k</property>

        <property name="useStreamOutput">0</property>

        <!--
            單位為m
        -->
        <property name="systemReserveMemorySize">384m</property>


        <!--是否採用zookeeper協調切換  -->
        <property name="useZKSwitch">true</property>


    </system>
    
    <!-- 全局SQL防火牆設置 -->
    <!-- 
    <firewall> 
       <whitehost>
          <host host="127.0.0.1" user="mycat"/>
          <host host="127.0.0.2" user="mycat"/>
       </whitehost>
       <blacklist check="false">
       </blacklist>
    </firewall>
    -->
    
    <user name="root">
        <property name="password">root</property>
        <property name="schemas">TESTDB</property>
        
        <!-- 表級 DML 權限設置 -->
        <!--         
        <privileges check="false">
            <schema name="TESTDB" dml="0110" >
                <table name="tb01" dml="0000"></table>
                <table name="tb02" dml="1111"></table>
            </schema>
        </privileges>        
         -->
    </user>

    <user name="user">
        <property name="password">user</property>
        <property name="schemas">TESTDB</property>
        <property name="readOnly">true</property>
    </user>

</mycat:server>

schema.xml

用途:管理邏輯表

為了演示方便,刪掉一些不必要的標籤,標籤詳細用法:點我直達

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

    <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100">
        <!-- auto sharding by id (long) -->
        <table name="cyb_test" dataNode="dn1,dn2,dn3" rule="mod-long" />
    </schema>
    <dataNode name="dn1" dataHost="localhost1" database="db1" />
    <dataNode name="dn2" dataHost="localhost1" database="db2" />
    <dataNode name="dn3" dataHost="localhost1" database="db3" />
    <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
              writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
        <heartbeat>select user()</heartbeat>
        <!-- can have multi write hosts -->
        <writeHost host="hostM1" url="192.168.31.200:3306" user="root"
                   password="root">
            <!-- can have multi read hosts -->
            <readHost host="hostS2" url="192.168.31.201:3306" user="root" password="root" />
        </writeHost>
    </dataHost>
</mycat:schema>

rule.xml

用途:定義了我們對錶進行拆分所涉及到的規則定義,視情況修改參數

<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License"); 
    - you may not use this file except in compliance with the License. - You 
    may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 
    - - Unless required by applicable law or agreed to in writing, software - 
    distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT 
    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the 
    License for the specific language governing permissions and - limitations 
    under the License. -->
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
    <tableRule name="rule1">
        <rule>
            <columns>id</columns>
            <algorithm>func1</algorithm>
        </rule>
    </tableRule>

    <tableRule name="rule2">
        <rule>
            <columns>user_id</columns>
            <algorithm>func1</algorithm>
        </rule>
    </tableRule>

    <tableRule name="sharding-by-intfile">
        <rule>
            <columns>sharding_id</columns>
            <algorithm>hash-int</algorithm>
        </rule>
    </tableRule>
    <tableRule name="auto-sharding-long">
        <rule>
            <columns>id</columns>
            <algorithm>rang-long</algorithm>
        </rule>
    </tableRule>
    <tableRule name="mod-long">
        <rule>
            <columns>id</columns>
            <algorithm>mod-long</algorithm>
        </rule>
    </tableRule>
    <tableRule name="sharding-by-murmur">
        <rule>
            <columns>id</columns>
            <algorithm>murmur</algorithm>
        </rule>
    </tableRule>
    <tableRule name="crc32slot">
        <rule>
            <columns>id</columns>
            <algorithm>crc32slot</algorithm>
        </rule>
    </tableRule>
    <tableRule name="sharding-by-month">
        <rule>
            <columns>create_time</columns>
            <algorithm>partbymonth</algorithm>
        </rule>
    </tableRule>
    <tableRule name="latest-month-calldate">
        <rule>
            <columns>calldate</columns>
            <algorithm>latestMonth</algorithm>
        </rule>
    </tableRule>
    
    <tableRule name="auto-sharding-rang-mod">
        <rule>
            <columns>id</columns>
            <algorithm>rang-mod</algorithm>
        </rule>
    </tableRule>
    
    <tableRule name="jch">
        <rule>
            <columns>id</columns>
            <algorithm>jump-consistent-hash</algorithm>
        </rule>
    </tableRule>

    <function name="murmur"
        class="io.mycat.route.function.PartitionByMurmurHash">
        <property name="seed">0</property><!-- 默認是0 -->
        <property name="count">2</property><!-- 要分片的數據庫節點數量,必須指定,否則沒法分片 -->
        <property name="virtualBucketTimes">160</property><!-- 一個實際的數據庫節點被映射為這麼多虛擬節點,默認是160倍,也就是虛擬節點數是物理節點數的160倍 -->
        <!-- <property name="weightMapFile">weightMapFile</property> 節點的權重,沒有指定權重的節點默認是1。以properties文件的格式填寫,以從0開始到count-1的整數值也就是節點索引為key,以節點權重值為值。所有權重值必須是正整數,否則以1代替 -->
        <!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property> 
            用於測試時觀察各物理節點與虛擬節點的分佈情況,如果指定了這個屬性,會把虛擬節點的murmur hash值與物理節點的映射按行輸出到這個文件,沒有默認值,如果不指定,就不會輸出任何東西 -->
    </function>

    <function name="crc32slot"
              class="io.mycat.route.function.PartitionByCRC32PreSlot">
        <property name="count">2</property><!-- 要分片的數據庫節點數量,必須指定,否則沒法分片 -->
    </function>
    <function name="hash-int"
        class="io.mycat.route.function.PartitionByFileMap">
        <property name="mapFile">partition-hash-int.txt</property>
    </function>
    <function name="rang-long"
        class="io.mycat.route.function.AutoPartitionByLong">
        <property name="mapFile">autopartition-long.txt</property>
    </function>
    <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
        <!-- how many data nodes -->
        <property name="count">3</property>
    </function>

    <function name="func1" class="io.mycat.route.function.PartitionByLong">
        <property name="partitionCount">8</property>
        <property name="partitionLength">128</property>
    </function>
    <function name="latestMonth"
        class="io.mycat.route.function.LatestMonthPartion">
        <property name="splitOneDay">24</property>
    </function>
    <function name="partbymonth"
        class="io.mycat.route.function.PartitionByMonth">
        <property name="dateFormat">yyyy-MM-dd</property>
        <property name="sBeginDate">2015-01-01</property>
    </function>
    
    <function name="rang-mod" class="io.mycat.route.function.PartitionByRangeMod">
            <property name="mapFile">partition-range-mod.txt</property>
    </function>
    
    <function name="jump-consistent-hash" class="io.mycat.route.function.PartitionByJumpConsistentHash">
        <property name="totalBuckets">3</property>
    </function>
</mycat:rule>

啟動mycat

進入mycat/bin,啟動mycat

啟動命令:./mycat start
停止命令:./mycat stop
重啟命令:./mycat restart
查看狀態命令:./mycat status

注意,可以使用mysql的客戶端直接連接mycat服務,默認端口為8066

錯誤日誌(重要)

  部署過程中,我碰到點小問題,找不到主機名,具體解決方案,請看我另一篇:點我直達 ,如果Mycat服務起不來,記得看錯誤日誌喲!

測試

ip:192.168.31.200(mysql主服務器)

ip:192.168.31.201(mysql從服務器)

ip:192.168.31.209(mycat服務器)

  注:演示過程中,因為mysql搭建了集群,主從複製,可能網絡原因,有些延遲,或者mysql主從複製同步機制問題,導致刷新好幾次,才显示出來,因為圖片較大,被分割幾張gif,內容都是連續的,驗證結果,達到預期,演示成功!

  MySQL集群搭建主從複製:點我直達

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

一文讀懂:防止過擬合的所有方法

什麼是過擬合

過擬合就是在訓練集上表現得非常好,在測試集上表現得不好。也就是我們俗稱的泛化能力弱

過擬合無法避免,只能緩解,那麼如何緩解呢?方法太多了。這篇文章一一介紹。

數據集增強Augmentation

圖像上,翻轉,平移,縮放,旋轉,鏡像,增強對比度,增強亮度等諸多方式。
我在下面的內容中介紹了圖像處理的圖像增強的方法:
【預處理庫函數】albumentations庫的簡單了解和使用

Early Stopping

訓練模型的時候,訓練誤差往往是不斷下降的,但是驗證數據集的誤差,是先下降後上升。 兩個數據集的誤差出現分歧的時候,說明模型開始過擬合了。所以Early Stopping就是當驗證數據集的誤差不在下降的時候,結束訓練,保存模型參數。

正則化regularization

L1正則:模型中只有少部分特徵對模型的泛化能力有貢獻,所以L1就是限制模型中非零參數的數量。讓大部分的模型參數都是0,只有真正對泛化能力其作用的參數才是非零的。

L2正則:我們希望模型找到的極小值是平坦的,為什麼呢?

圖中表示的意思,就是平坦的極小值,可以有更多的容忍,容忍什麼呢?容忍訓練數據集和測試數據集之前的分佈偏差。現在,如果模型的某些參數特別大,那麼就算輸入的樣本只有很小的區別,但是經過特別大的參數之後,模型給出的結果很可能是非常不同的。這就是太陡峭。所以L2正則就是限制模型參數的大小。參數的平方的和作為損失的一部分,當參數數值越大,那麼梯度下降的損失越大,就會強迫參數變小。

這裡有兩幅圖:

這一幅圖體現的是假設只有兩個參數的情況下,增加L1正則的情況。圓圈圈體現的是損失等值線,方框是L1正則的損失。假設沒有L1正則,那麼參數應該收斂到最小的那個圓圈中的。但是因為增加了L1正則,所以需要權衡兩個部分的損失,然後找到接觸的交點位置。因為圓形和矩形在矩形的頂點相交的可能性大,而矩形的頂點就是某一個參數為0的情況。所以L1正則會讓模型參數有更大的可能性為0.
【在更多參數的模型中,會有更多的頂點。不過二維圖像就畫不出來了】

這個是L2正則的示意圖。L2正則式一個原型因為是參數的平方和。相比L1的(0,1)這樣的交點,L2更希望每一個參數都普遍較小,不希望某一個參數特別大。

Dropout

這個就是神經網絡中,在全連接網絡中經常用到的。

在每一個Batch數據訓練的時候,Dropout層按照概率P隨機讓一些神經元失活,然後保留下來的神經元的參數被更新。dropout是只有在訓練的時候才使用的,在測試的時候並不適用。

我個人理解的dropout其實就相當於一個多模型融合的過程。因為每一次都會失活一部分的神經元,所以每一次的模型都是不那麼一樣的,相當於不同的模型吧。

增加噪音

輸入中增加噪音

輸入中有噪音\(\epsilon\),那麼輸出中就會有一個類似於\(\epsilon \omega\),這樣的損失項。 從而限制權值的大小。

當然這樣也可以增加模型對輸入的容忍度,我覺得也可以理解為一種數據增強。 去噪自編碼器DAE就是利用這樣的方法的。

權值中加噪音

這個用的不多,在初始化網絡的時候,用0均值的高斯分佈作為參數的初始化。

集成

集成主要是bagging,boosting,之前說的dropout我覺得也可以算作集成的方法

bagging

將數據集抽取一部分,比如抽取70%的樣本,然後用這些樣本去訓練一個模型。然後再從數據集中抽取70%的樣本,再訓練一個新的。典型的就是隨機森林。
【神經網絡因為訓練速度的問題,所以一般不用這樣的方法。決策樹lgb啥的可以用】

boosting

訓練複雜神經網絡比較慢,所以可以通過訓練多個簡單的分類器,然後加權平均每一個分類器的輸出。這就是Boost的思想。【這句話給我背下來!】

之後整理一下Adaboost和XGBoost的這些算法。

其他

  • 限制網絡的層數和複雜度

**END**

喜歡的話請關注我們的微信公眾號~《你好世界煉丹師》或者知乎【你好世界煉丹師】。

  • 公眾號主要講統計學,數據科學,機器學習,深度學習,以及一些參加Kaggle競賽的經驗。
  • 公眾號內容建議作為課後的一些相關知識的補充,飯後甜點。
  • 此外,為了不過多打擾,公眾號每周推送一次,每次4~6篇精選文章。

微信搜索公眾號:你好世界煉丹師。期待您的關注。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

北加州野火再起安全性斷電 2000多人撤離

摘錄自2019年10月24日中央社報導

北加州酒鄉有超過一萬公頃的土地遭到森林野火侵襲,2000多人被迫離家,目前沒有人員傷亡,以公共安全為理由的民生斷電又展開。美國媒體ABC News在推特放上現場畫面。

這場名為金凱德(Kincade)的火災,起火地點距離太平洋瓦斯電力公司(Pacific Gas and Electric Company,PG&E)切斷電源的索諾馬郡(Sonoma County)不遠,索諾馬是與納帕山谷(Napa Valley)齊名的北加州葡萄酒鄉。

今年季節性的高熱乾風再度來襲,PG&E近日兩度以公共安全為考量,在非常短的時間、突襲式通知警戒區域的民眾斷電消息,引發擾民爭議,也被批評沒有及早檢查、更新輸電線等相關設備。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

迎接Model 3試產,Tesla發股債籌資11.5億美元

美國電動車大廠特斯拉(Tesla)15 日公布最新增資計畫,由於即將進入Model 3 電動車的試產階段,預計將透過發行股票和優先債券融資11.5 億美元(約355 億台幣)。

華爾街日報(Wall Street Journal)報導,特斯拉預計將發行2.5 億美元普通股及7.5 億美元可轉換優先債券來籌措資金,執行長馬斯克(Elon Musk)本人也將認購2,500 美元的普通股,所得資金將用來強化資產負債表,並降低近期業務擴張可能帶來的風險。

特斯拉表示,2016 年共生產了8.4 萬輛汽車,未來也將持續投資、創造產品並提升產能,希望至2018 年底總產量能達到50 萬輛,2020 年則達到100 萬輛。在公布計畫後,特斯拉盤後股價上漲2.3% 至261.5 美元。

報導指出,不僅是汽車,馬斯克也計畫將電動車運用在更多車款上,如公共汽車及載貨卡車;隨著去年收購SolarCity Corp,馬斯克希望未來將特斯拉打造成一家永續能源公司,提供包含電動車、電池及太陽能發電在內相關產品。

AutoCar 網站則提到,除了今年將試產的Model 3 電動車,特斯拉也已經在著手進行另一部迷你SUV 車款Model Y 的開發,相對於特斯拉其他車款來說,Model Y 和Model 3 一樣將會相對平價,市場估計Model 3 售價約為3.7 萬美元(約113 萬台幣),考量到迷你SUV 的機能與客群的不同,Model Y 的售價可能將再比Model 3 車款高上數千美元。

 

▲由 Autocar 製作的 Model Y 概念圖

(合作媒體:。首圖來源:Tesla)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

程泰打入Tesla Model 3供應鏈,預計年底出貨

程泰16日參加宏遠證券所舉辦自動化產業投資論壇,對於2017年營運展望,程泰表示,目前母公司程泰集團在手訂單已回升至5億多元,且未含3月台北國際工具機展部分,同時在中國大陸訂單約有2-3億元,整體而言,目前整體訂單能見度看到第二季底。程泰集團董事長楊德華則表示,集團整體在手訂單規模約22億至23億元,至於3月台北國際工具機展接單規模約在3億至5億元。

程泰也指出,公司業務以車床為主,由於波音預期未來20年全球航太市場規模將達180兆元,預估民航機需求約3.8萬架,加上政府要推國機國造,因此未來也會聚焦在航太產業。此外,在電動車布局方面,程泰打入美國特斯拉電動車Model 3供應鏈,於台中大里工業區建立代工基地,預計今年底起,每月將出貨減速齒輪箱的齒輪傳動軸前端車銑磨6萬套。

程泰集團旗下包括程泰及亞崴兩工具機廠,亞崴表示,農曆年後,台灣航太及汽車零組件業開始投資擴廠,加上推進國機國造及航太零件國產化,預期今年台灣市場會有不錯表現,目前台灣地區在手訂單14億元,大陸吳江及嘉興廠在手訂單3-4億元,兩岸合併在手訂單約18億元,訂單能見度看到7月;在產能布局方面,嘉義大埔美一期新廠預計今年底完工,大陸吳江廠亦規劃最快於今年底興建二期廠。

(本文內容由授權使用。圖片出處:Tesla)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

博弈論——兩人取子遊戲與威佐夫博弈,隱藏在背後的黃金分割

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天是算法和數據結構專題第25篇文章,我們繼續博弈論專題。

在上一篇文章當中我們了解了最簡單的巴什博奕,今天我們來看看另一個經典的博弈模型——威佐夫博弈。博弈論和機器學習有些類似,數學家們針對場景進行建模,設計出了幾個經典模型。然後我們在面臨具體問題的時候,對問題進行深入分析,尋找最合適的模型應用來解決它。

石子問題

我們來看一道經典的例題,有兩堆石子,有兩個絕頂聰明的人在玩一個遊戲。每次每個人可以從其中一堆石子當中取走任意數量的石子,或者是從兩堆當中同時取走相同數量的石子。無法取石子的人落敗,請問,在告知兩堆石子數量的情況下,這兩個人當中哪一方會獲勝?

我們簡單分析一下,會發現一些局面是先手必敗的。比如說(0, 0),再比如(1, 2)。我們簡單分析一下(1, 2),先手有4種策略,首先他可以取走第一堆,那麼後手可以取完第二堆,顯然後手獲勝。他也可以在第二堆當中取1個,這時剩下(1, 1),後手會同時取完,同樣是後手獲勝。第三種是他取走第二堆,後手可以取完第一堆,後手獲勝。第四種是他在第一堆和第二堆當中同時取走一個,這時第二堆剩下一個,後手勝。

那麼,這些必敗的狀態之間有什麼規律呢?我們怎麼找到這個規律,並且找到解呢?

分析

我們可以枚舉幾個必敗的狀態:(0, 0), (1, 2), (3, 5), (4, 7)…

我們觀察一下這些狀態,可以找到兩條規律。我們假設從小到大排的第k個必敗狀態是(x, y),並且x < y。我們可以發現y = x + k。也就是說必敗狀態兩個數的差值是遞增的,這也說明了每一個必敗狀態的差值都各不相同。

其實這是很容易證明的,我們用反證法,假設(a, a+k), (b, b+k)都是必敗狀態,並且a < b。那麼先手在面臨(b, b+k)的時候,只需要在兩堆當中同時取走b-a個石子,那麼給後手的局面就是(a, a+k)。對於後手來說,這是一個必敗的局面,這就和(b, b+k)先手必敗矛盾,所以不存在兩個必敗局面的差值相等

我們也可以作圖分析,我們把兩堆石子的數量看成是坐標軸上的一個點。所以遊戲就變成了:棋盤上有一個點,每次每個人可以將它向下、向左或者向左下移動若干個格子,不能移動的人輸。終止節點顯然是原點,一步就能移動到原點的點顯然是必勝點,假設我們給這些所有必勝點都染色的話,剩下的的沒當中橫縱坐標和最小的點就是下一個必敗點。因為它不論如何移動,都會給對手留下一個必勝點。

我們根據上面的邏輯把必敗點都染色,可以得到下面這張圖:

從這張圖可以看出,必敗點之間不能通過一次移動得到,換句話說可以一次移動到必敗點的點都是必勝點,從圖上可以看出,除了必敗點的之外的點都是必勝點,並且每一個自然數都必然只會被包含在一個必敗狀態當中。

到這裏,我們距離解法已經很接近了,現在剩下的問題是,我們如何根據x和y的取值快速判斷它們是否構成一個必敗局面呢?也就是說我們能不能找出一個通項公式,對於第k個必敗局面,它的坐標是(\(x_k, y_k\))呢?

求解

為了寫出通項公式,我們需要引入Betty定理

設a和b是兩個正無理數,並且\(\frac{1}{a} + \frac{1}{b} = 1\)

記P={[\(a_n\)], \(n \in N^+\)}, Q={[\(b_n\)], \(n \in N^+\)},則\(P \cap Q = \varnothing\)\(P\cup Q = N^+\)

證明

\(P\cap Q = \varnothing\)

反證,我們假設存在\(k \in P\)並且\(k \in Q\),即存在正整數n, m滿足 k < an, bm < k+1。

也就是:\(\frac{n}{k} > \frac{1}{a} > \frac{n}{k+1}, \frac{m}{k} > \frac{1}{b} > \frac{m}{k+1}\)兩個式子相加可以得到:\(\frac{m+n}{k} > 1 > \frac{m+n}{k}\)

\(k < n+m < k+ 1\),這與n,m,k都是正整數矛盾

\(P \cup Q = N^+\)

反證,假設存在\(k \notin P\)\(k \notin Q\),即存在正整數n,m滿足\(an < k < a(n+1)-1, bm < k < b(m+1)-1\)

即:\(\frac{n}{k} < \frac{1}{a} < \frac{n+1}{k+1}, \frac{m}{k} < \frac{1}{b} < \frac{m+1}{k+1}\)

相加,可以得到:\(\frac{m+n}{k} < 1 < \frac{n+m+2}{k+1}\)

即:n + m < k < n + m + 1,這與n,m,k均為正整數矛盾

我們花了這麼大力氣來證明Betty定理就是為了用的,因為我們發現必敗狀態的通項和Betty定理序列很像。我們不妨假設存在這樣的a, b同時滿足Betty定理與必敗狀態的性質:

\[[an] + n = [bn], \frac{1}{a} + \frac{1}{b} = 1 \]

\[[an] + n = [an + n] = [(a+1)n] = [bn] \]

代入可以得到:

\[\frac{1}{a} + \frac{1}{a+1} = 1 \]

解這個方程,可以得到\(a = \frac{1 + \sqrt{5}}{2}\approx 1.618\),熟悉數學的同學相信一下就看出來了,這個數是黃金分割的比例,這是巧合嗎,還是藏着更深的道理呢?

至少,求出了a之後,我們就可以非常簡單地判斷必敗狀態了:

import math
def lose_or_win(a, b):
    if a > b:
        a, b = b, a
    
    k = b - a
    # 根據差值k求出第k個必敗狀態,判斷是否相等
    return not (int(k * (math.sqrt(5)+1) / 2)) == a

總結

和之前介紹的巴什博奕相比,威佐夫博弈的推導過程要複雜得多,但是雖然推導過程依然複雜,但是仍然擋不住最後實現的代碼非常簡單。

另外,在推導的過程當中,我們用到了Betty定理,這個定理的推導和證明雖然不難,但是如果不是數學專業的同學,可能大概率都沒有接觸過。這其實體現了博弈論本身和數學的關係是非常緊密的。一個看起來非常簡單的問題,引申出了一系列眼花繚亂的推導和證明,怎麼樣,大家看得還過癮嗎?

今天的文章到這裏就結束了,如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

Task.Result跟 Task.GetAwaiter.GetResult()相同嗎?怎麼選?

前幾天在用線程池執行一些任務時運到一種情形,就是回調方法中使用到了異步方法,但是回調方法貌似不支持async await的寫法。這時候我應該如何處理呢?是使用Task.Result來獲取返回結果,還是使用GetAwaiter.GetResult()呢?本文就來探討下吧。

作者:依樂祝

原文地址:https://www.cnblogs.com/yilezhu/p/13168337.html

這裏先上我這種場景的偽代碼:

ThreadPool.QueueUserWorkItem(ExcuteScanProcess, node);

ExcuteScanProcess這個回調方法中

private void ExcuteScanProcess(object state)
{
    ……其他處理……
    repository.UpdateAsync(node).ConfigureAwait(false).GetAwaiter().GetResult();
    ……其他處理……
}

如上圖所示repository.UpdateAsync(node)屬於一部方法,這時候我想要等待它異步執行完成之後再執行後續的邏輯。這時候我有兩種選擇,是直接

repository.UpdateAsync(node).ConfigureAwait(false).GetAwaiter().GetResult();

好呢,還是

repository.UpdateAsync(node).ConfigureAwait(false).Result;

好呢?

為此我查找了相關的資料,對它倆的區別做一個簡單的總結:

其實這兩個使用方式是差不多的。不過,還是有一點小小的區別的:如果任務失敗,Task.GetAwaiter().GetResult()會直接拋出異常,而Task.Result則會把異常包裝在AggregateException中。從這個角度說Task.GetAwaiter().GetResult()要優於Task.Result。畢竟它少了異常的包裝操作,即直接拋出異常,而不是把異常包裝在AggregateException中。

下面的引言解釋了為什麼Task.Result不僅僅包含Task.GetAwaiter().GetResult()(由於“非常高的兼容性”)的異常傳播行為。

如前所述,我們有一個非常高的兼容性標準,因此我們避免了改動。因此,Task.Wait保留了始終包裝的原始行為。但是,您可能會發現自己處在某些高級情況下,這些情況下您想要的行為類似於所採用的同步阻塞Task.Wait,但是您希望將原始異常展開而不是傳播,而不是將其封裝在AggregateException中。為此,您可以直接定位任務的等待者。當您編寫“ await task;”時,編譯器Task.GetAwaiter()會將其轉換為方法的用法,這將返回具有GetResult()方法的實例。當用於有故障的任務時,GetResult()將傳播原始異常(這是“ await task;” 如何獲得其行為)。因此,您可以使用“task.GetAwaiter().GetResult()如果您想直接調用此傳播邏輯。

https://blogs.msdn.microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/

GetResult”實際上表示“檢查任務是否有錯誤”

通常,我會儘力避免對異步任務進行同步阻塞。但是,在少數情況下,我確實違反了該準則。在那些罕見的情況下,我的首選方法是GetAwaiter().GetResult()因為它保留任務異常,而不是將它們包裝在中AggregateException

總結

通過上述內容的闡述,因此在那些必須對異步任務進行同步阻塞的場景中,我選擇使用GetAwaiter().GetResult()

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

這一次搞懂Spring Web零xml配置原理以及父子容器關係

前言

在使用Spring和SpringMVC的老版本進行開發時,我們需要配置很多的xml文件,非常的繁瑣,總是讓用戶自行選擇配置也是非常不好的。基於約定大於配置的規定,Spring提供了很多註解幫助我們簡化了大量的xml配置;但是在使用SpringMVC時,我們還會使用到WEB-INF/web.xml,但實際上我們是完全可以使用Java類來取代xml配置的,這也是後來SpringBoott的實現原理。本篇就來看看Spring是如何實現完全的零XML配置。

正文

先來看一下原始的web.xml配置:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      <!--加載spring配置-->
      classpath:spring.xml
    </param-value>
  </context-param>
  <context-param>
    <param-name>webAppRootKey</param-name>
    <param-value>ServicePlatform.root</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    <!--<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>-->
  </listener>

  <servlet>
    <servlet-name>spring-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <!--springmvc的配置文件-->
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-dispatcher.xml</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>spring-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

這裏各個配置的作用簡單說下,context-param是加載我們主的sping.xml配置,比如一些bean的配置和開啟註解掃描等;listener是配置監聽器,Tomcat啟動會觸發監聽器調用;servlet則是配置我們自定義的Servlet實現,比如DispatcherServlet。還有其它很多配置就不一一說明了,在這裏主要看到記住context-paramservlet配置,這是SpringIOC父子容器的體現。在之前的I文章中講過IOC容器是以父子關係組織的,但估計大部分人都不能理解,除了看到複雜的繼承體系,並沒有看到父容器作用的體現,稍後來分析。
了解了配置,我們就需要思考如何替換掉這些繁瑣的配置。實際上Tomcat提供了一個規範,有一個ServletContainerInitializer接口:

public interface ServletContainerInitializer {
    void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;
}

Tomcat啟動時會調用該接口實現類的onStartup方法,這個方法有兩個參數,第二個不用說,主要是第一個參數什麼?從哪裡來?另外我們自定義的實現類又怎麼讓Tomcat調用呢?
首先解答最後一個問題,這裏也是利用SPI來實現的,因此我們實現了該接口后,還需要在META-INF.services下配置。其次,這裏傳入的第一個參數也是我們自定義的擴展接口的實現類,我們可以通過我們自定義的接口實現很多需要在啟動時做的事,比如加載Servlet,但是Tomcat又是怎麼知道我們自定義的接口是哪個呢?這就需要用到@HandlesTypes註解,該註解就是標註在ServletContainerInitializer的實現類上,其值就是我們擴展的接口,這樣Tomcat就知道需要傳入哪個接口實現類到這個onStartup方法了。來看一個簡單的實現:

@HandlesTypes(LoadServlet.class)
public class MyServletContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        Iterator var4;
        if (set != null) {
            var4 = set.iterator();
            while (var4.hasNext()) {
                Class<?> clazz = (Class<?>) var4.next();
                if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()) && LoadServlet.class.isAssignableFrom(clazz)) {
                    try {
                        ((LoadServlet) clazz.newInstance()).loadOnstarp(servletContext);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

public interface LoadServlet {

    void loadOnstarp(ServletContext servletContext);
}

public class LoadServletImpl implements LoadServlet {
    @Override
    public void loadOnstarp(ServletContext servletContext) {
        ServletRegistration.Dynamic initServlet = servletContext.addServlet("initServlet", "org.springframework.web.servlet.DispatcherServlet");
        initServlet.setLoadOnStartup(1);
        initServlet.addMapping("/init");
	}
}

這就是Tomcat給我們提供的規範,通過這個規範我們就能實現Spring的零xml配置啟動,直接來看Spring是如何做的。
根據上面所說我們可以在spring-web工程下找到META-INF/services/javax.servlet.ServletContainerInitializer配置:

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<>();

		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				// Be defensive: Some servlet containers provide us with invalid classes,
				// no matter what @HandlesTypes says...
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}

		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}

}

核心的實現就是WebApplicationInitializer,先看看其繼承體系

AbstractReactiveWebInitializer不用管,主要看另外一邊,但是都是抽象類,也就是說真的實例也是由我們自己實現,但需要我們實現什麼呢?我們一般直接繼承AbstractAnnotationConfigDispatcherServletInitializer類,有四個抽象方法需要我們實現:

    //父容器
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{SpringContainer.class};
    }

    //SpringMVC配置子容器
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{MvcContainer.class};
    }

    //獲取DispatcherServlet的映射信息
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

	// filter配置
    @Override
    protected Filter[] getServletFilters() {
        MyFilter myFilter = new MyFilter();
        CorsFilter corsFilter = new CorsFilter();
        return new Filter[]{myFilter,corsFilter};
    }

這裏主要注意getRootConfigClassesgetServletConfigClasses方法,分別加載父、子容器:

@ComponentScan(value = "com.dark",excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
public class SpringContainer {
}

@ComponentScan(value = "com.dark",includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
public class MvcContainer {
}

看到這兩個類上的註解應該不陌生了吧,父容器掃描裝載了所有不帶@Controller註解的類,子容器則相反,但需要對象時首先從當前容器中找,如果沒有則從父容器中獲取,為什麼要這麼設計呢?直接放到一個容器中不行么?先思考下, 稍後解答。
回到onStartup方法中,直接回調用到AbstractDispatcherServletInitializer類:

	public void onStartup(ServletContext servletContext) throws ServletException {
		super.onStartup(servletContext);
		//註冊DispatcherServlet
		registerDispatcherServlet(servletContext);
	}

先是調用父類:

	public void onStartup(ServletContext servletContext) throws ServletException {
		registerContextLoaderListener(servletContext);
	}

	protected void registerContextLoaderListener(ServletContext servletContext) {

		//創建spring上下文,註冊了SpringContainer
		WebApplicationContext rootAppContext = createRootApplicationContext();
		if (rootAppContext != null) {
			//創建監聽器
			ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
			listener.setContextInitializers(getRootApplicationContextInitializers());
			servletContext.addListener(listener);
		}
	}

然後調用createRootApplicationContext創建父容器:

	protected WebApplicationContext createRootApplicationContext() {
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
			context.register(configClasses);
			return context;
		}
		else {
			return null;
		}
	}

可以看到就是創建了一個AnnotationConfigWebApplicationContext對象,並將我們的配置類SpringContainer註冊了進去。接着創建Tomcat啟動加載監聽器ContextLoaderListener,該監聽器有一個contextInitialized方法,會在Tomcat啟動時調用。

	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}

	 */
	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		long startTime = System.currentTimeMillis();
		try {
			// Store context in local instance variable, to guarantee that
			// it is available on ServletContext shutdown.
			if (this.context == null) {
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent ->
						// determine parent for root web application context, if any.
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			return this.context;
		}
	}

可以看到就是去初始化容器,這個和之前分析xml解析是一樣的,主要注意這裏封裝了ServletContext對象,並將父容器設置到了該對象中。
父容器創建完成后自然就是子容器的創建,來到registerDispatcherServlet方法:

	protected void registerDispatcherServlet(ServletContext servletContext) {
		String servletName = getServletName();
		Assert.hasLength(servletName, "getServletName() must not return null or empty");

		//創建springmvc的上下文,註冊了MvcContainer類
		WebApplicationContext servletAppContext = createServletApplicationContext();
		Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");

		//創建DispatcherServlet
		FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
		Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
		dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

		ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
		if (registration == null) {
			throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
					"Check if there is another servlet registered under the same name.");
		}

		/*
		* 如果該元素的值為負數或者沒有設置,則容器會當Servlet被請求時再加載。
			如果值為正整數或者0時,表示容器在應用啟動時就加載並初始化這個servlet,
			值越小,servlet的優先級越高,就越先被加載
		* */
		registration.setLoadOnStartup(1);
		registration.addMapping(getServletMappings());
		registration.setAsyncSupported(isAsyncSupported());

		Filter[] filters = getServletFilters();
		if (!ObjectUtils.isEmpty(filters)) {
			for (Filter filter : filters) {
				registerServletFilter(servletContext, filter);
			}
		}

		customizeRegistration(registration);
	}

	protected WebApplicationContext createServletApplicationContext() {
		AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
		Class<?>[] configClasses = getServletConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			context.register(configClasses);
		}
		return context;
	}

這裏也是創建了一個AnnotationConfigWebApplicationContext對象,不同的只是這裏註冊的配置類就是我們的Servlet配置了。然後創建了DispatcherServlet對象,並將上下文對象設置了進去。看到這你可能會疑惑,既然父子容器創建的都是相同類的對象,何來的父子容器之說?別急,這個在初始化該上文時就明白了。但是這裏的初始化入口在哪呢?沒有看到任何監聽器的創建和調用。實際上這裏的上下文對象初始化是在Servlet初始化時實現的,即init方法,直接來到HttpServletBeaninit方法(分析SpringMVC源碼時講過):

	public final void init() throws ServletException {
		...省略
		
		// Let subclasses do whatever initialization they like.
		initServletBean();
	}

	protected final void initServletBean() throws ServletException {
		try {
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
	}

	protected WebApplicationContext initWebApplicationContext() {
		//這裡會從servletContext中獲取到父容器,就是通過監聽器加載的容器
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					if (cwac.getParent() == null) {
						cwac.setParent(rootContext);
					}
					//容器加載
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			synchronized (this.onRefreshMonitor) {
				onRefresh(wac);
			}
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}

看到這裏想你也應該明白了,首先從ServletContext中拿到父容器,然後設置到當前容器的parent中,實現了父子容器的組織,而這樣設計好處我想也是很清楚的,子容器目前裝載的都是MVC的配置和Bean,簡單點說就是Controller,父容器中都是Service,Controller是依賴於Service的,如果不構建這樣的層級關係並優先實例化父容器,你怎麼實現Controller層的依賴注入成功呢?

總結

本篇結合之前的文章,分析了SpringMVC零XML配置的實現原理,也補充了之前未分析到父子容器關係,讓我們能從細節上更加全面的理解SpringIOC的實現原理,相信看完本篇對於SpringBoot的實現你也會有自己的想法。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

流產、死胎層出不窮 南蘇丹隱匿石油業環境報告 犧牲者至今未受保障

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

2017中國國際新能源汽車產業博覽會

展會城市:
深圳、上海
展會開始日期:
深圳:2017/6/16
上海:2017/8/23
展會結束日期:
深圳:2017/6/18
上海:2017/8/25

2017新能源汽車產業博覽會將於2017年6月16-18日,8月23-25日分別在深圳會展中心及上海新國際博覽中心舉行,本屆展會預計累計展示面積達7萬平米,展商超過800家,專業觀眾超過6萬人次,全產業鏈展示實現無縫對接。

展會以展覽展示及技術交流為主題,組委會將重點邀請新能源汽車生產企業及核心三電(電池、電機、電控)企業參與。新能源汽車領域包括:比亞迪、北汽、上汽、宇通、金龍、青年客車、福田、中通、東風、力帆等;電池領域包括:國軒、寧德時代、力神、沃特瑪、邁科等;電機電控領域包括:寶馬、中國南車、大洋電機、中科三環、上海大郡、上海電驅動、浙江尤奈特、深圳大地和、大連機電、東風機電、南洋機電、精進電動等。

振威展覽【股票代碼:834316】是中國規模最大的民營展覽公司,深耕行業20年,于北京、天津、廣州、西安、成都、新疆分別設有全資子公司。每年承接20多個大型政府展會,累積了豐富的政府資源和關係網絡。為打通新能源汽車產業各大環節的壁壘,振威展覽聯合中國土木工程學會城市公共交通學會、廣東省充電設施協會、充電設施線上網,共同打造中國新能源汽車產業第一展。

主辦方積累了豐富的資源,本屆展會得到了上海市經信委、江蘇省經信委、浙江省經信委、武漢市經信委、廣州市經信委等政府部門的高度關注與大力支持。此外,展會還將重點邀請各地公交集團、旅遊公司、計程車運營公司、汽車租賃企業、大型物流公司、市政環衛公司等組團參觀交流,迅速實現供需對接。

兩地展會同期均舉辦2017振威新能源汽車產業峰會,峰會圍繞當前最熱門話題展開討論。如何更好的實現市場驅動,如何提高整個產業的核心競爭力,都是需要我們共同思考的問題。為此,組委會將邀請來自新能源汽車產業各個領域的知名專家學者、領軍企業高層與政府代表、協會代表,共同探討新形勢下如何更好的提升市場化水準,如何提升企業核心競爭力、深化全產業鏈的交流與合作,實現共贏的局面。

參展範圍

新能源汽車

純電動車:純電動客車、純電動城市物流車、純電動乘用車、純電動環衛車等;
混合動力車:混合動力轎車、插電混合動力大巴等;
其他:氫燃料電池汽車、增程式電動車等;

新能源汽車核心零部件及配套

動力集成:電動機、整車控制器、功率轉換器、傳動裝置、電池組、BMS、啟停電源、移動車載充電器、充電樁等;
解決方案:整車設計、輕量化底盤、變速動力系統、直驅動力系統等;
配套產品:磁性材料、連接器、飛輪、超級電容、相關新材料、相關檢測設備等。

聯繫方式:
廣州振威國際展覽有限公司
電話:+86 20 8395 3286
手機(微信):+86 135 3358 3989
聯絡人:黃俊鵬
E-mail:Johnny@zhenweiexpo.com

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心