厄瓜多總統與原住民領袖達協議 化解反撙節示威

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

莫雷諾為向國際貨幣基金貸款42億美元而取消燃油補貼後,國內物價迅速飆升,引發長達12天的示威,造成7人喪命。暴力衝突迫使莫雷諾將政府遷移至厄瓜多第2大城瓜亞基爾(Guayaquil),並嚴重影響石油業,能源部暫停超過2/3原油的配送。抗議人士甚至佔領亞馬遜雨林地區的3處石油設施。

厄瓜多總統莫雷諾及原住民領袖瓦爾加斯13日達成協議,終結近2週來反對撙節措施的暴力抗議。政府是為為獲得國際貨幣基金(IMF)數十億美元貸款,而採取這些緊縮措施。

法新社報導,聯合國官員代表宣讀的聯合聲明說,「根據這項協議,厄瓜多各地的群眾動員劃下句點,我們承諾會恢復國內和平」。聲明並表示,政府已撤回取消燃油補貼的命令。臉上塗著油彩、頭上頂著羽毛頭飾的瓦爾加斯證實:「這些適用於我們全國各地的措施已經取消了。」

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

【其他文章推薦】

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

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

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

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

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

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

MapReduce 論文閱讀筆記

目錄

  • Abstract
  • Introduction
  • 2 Programming Model
    • 2.1 Example
    • 2.2 Types
    • 2.3 More Examples
  • 3 Implementation
    • 3.1 Execution Overview
    • 3.2 Master Data Structures
    • 3.3 Fault Tolerance
      • Worker Failure (工作節點故障)
      • Master Failure(主節點故障)
      • Semantics in the Presence of Failures(語義可能存在的故障)
    • 3.4 Locality
    • 3.5 Task Granularity
    • 3.6 Backup Tasks
  • 4 Refinements
    • 4.1 Partitioning Function
    • 4.2 Ordering Guarantees
    • 4.3 Combiner Function
    • 4.4 Input and Output Types
    • 4.5 Side-effects
    • 4.6 Skipping Bad Records
    • 4.7 Local Execution
    • 4.8 Status Information
    • 4.9 Counters
  • Others

Abstract

MapReduce :

  • programming model 編程模型
  • an associated implementation for processing and generating large data sets.

用戶只需要指定 Map(Map函數將 key/value 類型的 pair 生成中間結果的 pair) 和 Reduce 函數(Reduce 函數將所有具有相同中間結果的值組合起來)即可。

MapReduce 封裝隱藏了分佈式系統并行計算的細節:

  • 輸入數據的分割
  • 計劃將程序分配到一組計算機中
  • 處理機器故障
  • 管理集群內部的通信

程序(in functional style)分佈式的運行在大型分佈式的集群上,而且具有很好的可伸縮性 scalable。

Introduction

過去這些年,Google一直在尋找方法來實現處理大量數據(抓取到的文件,web日誌等)的方法,通常數據量很大而且必須分散在數以千計的電腦上來進行運算。為了處理如何使計算相互關聯,分配數據以及處理故障的問題,往往編寫大量的複雜代碼掩蓋了他們,最初的簡單計算的初衷卻被忽略掉。

為了解決這種複雜性,抽象出了一個簡單的計算模型放到一個庫中,這個庫隱藏了可能出現的問題:

  • 并行計算
  • 容錯
  • 數據分發
  • 負載均衡

這個抽象受到了 Lisp 以及很多函數式編程語言中存在的原語 mapreduce 的啟發。

大多數并行計算都包含兩個步驟:

  • map:將每個邏輯記錄變成 key/value 的中間形式方便計算
  • reduce:將所有具有相同 key 的值組合到一起來進行合適的處理

我們使用一個函數式的編程模型(functional programming model)可以讓處理大型的并行計算和使用重新執行作為容錯的主要機制變得很簡單。

這項工作的主要貢獻是:提供了一個簡單但是很強大的接口(interface)讓自動化的并行計算和大規模計算的分發成為可能,結合該接口的實現,可以在商用機的大型集群上實現高性能。

Section2 :描述了基本的編程模型給出幾個例子

Section3 :描述MapReduce 接口針對集群運算環境的實現

Section4 :一些針對該模型的細微的改良

Section5 :針對實現設計出一系列性能衡量方法

Section6 :MapReduce 在 Google 中的使用,以及使用 MapReduce 來重寫生產環境的索引系統

Section7 :相關以及未來的工作

2 Programming Model

input: a set of key/value pairs

output: a set of key/value pairs

MapReduce 的用戶將只會使用兩個函數 MapReduce

Map:用戶編寫,將輸入的 pair 變成 k/v 的中間 pairs,然後 MapReduce 會把具有相同 key 的 pair 送給 Reduce 函數

Reduce: 用戶編寫,接受中間結果 key 和 key 的一系列值。將這些值組合起來成為更少的 k/v;通常每個 Reduce 函數只輸出一個 或者 0 個值。中間結果太多無法全部放到內存中,可以通過迭代的方法來處理大量的 value

2.1 Example

設想一個需要統計文件中每個單詞數量的一個問題,我們很可能編寫這樣的代碼:

map(String key, String value):
	// key: document name
	// value: document contents
	for each word w in value:
		EmitIntermediate(w, "1")
      
reduce(String key, String values):
	// key: a word
	// values: a list of counts
	int result = 0
  for each v in values
    result += ParseInt(v)
  Emit(AsString(result))

map:給每個單詞添加一個屬性(出現的次數,這裏就是1)

reduce:給每個特定的單詞加起來計算總數並且提交

此外,用戶編寫代碼以使用輸入和輸出文件的名稱以及可選的調整參數來填充mapreduce規範對象。然後,用戶調用MapReduce函數,並將其傳遞給指定對象。用戶代碼與MapReduce庫(在C ++中實現)鏈接在一起。

2.2 Types

儘管前面偽代碼使用 string 來寫的輸入輸出,但是從概念上說,是由用戶來指定 map 和 reduce 的類型

map (k1, v1)  ->  list(k2, v2)
map (k2, list(v2)) -> list(v2)

輸入值和中間值來自不同的域,中間值和輸出值來自相同的域

C ++實現在用戶定義的函數之間來回傳遞字符串,並將其留給用戶代碼以在字符串和適當的類型之間進行轉換

2.3 More Examples

這有一些可以使用 MapReduce 簡化的計算:

Distributed Grep: 分佈式的匹配,map 函數提交一個符合匹配的 line, reduce 的作用只是複製中間結果到輸出

Count of URL Access Frequency: 網頁訪問計數,map 處理網頁請求並且輸出中間結果為 <URL, 1>, reduce 功能是將所有的相同的 URL 計算到一起提交為 <URL, total count>

Reverse Web-Link Graph: 翻轉網絡鏈接圖, map 輸出 <target, source> pairs ,將 target 命名為 source。reduce函數連接與給定目標URL關聯的所有源URL的列表,並提交該對 <target, list(source)>

Term-Vector per Host: 術語向量是出現在一篇文章中最重要的術語集合列表<word, frequency> pairs。map 函數給每個輸入文件輸出一個 <hostname, term vector> pairs,reduce 函數傳遞給特定主機的術語向量,然後去掉不常出現的向量最後提交一個 <hostname, term vector> pair

Invert Index:map函數解析每個文檔,併發出一系列<單詞,文檔ID>對。 reduce函數接受給定單詞的所有對,對相應的文檔ID進行排序,併發出一個“單詞,列表(文檔ID)”對。setofall輸出對形成一個簡單的倒排索引。易於擴展此計算以跟蹤單詞位置。

Distributed Sort: 分佈式排序,map函數功能從每個記錄中提取鍵,併發出一個<key, record>對。 reduce函數將所有對保持不變。這種計算取決於第4.1節中描述的分區功能和第4.2節中描述的排序屬性。

3 Implementation

MapReduce 可以有很多不同的實現,正確實現是根據你自己所在的環境來進行實現,例如某個實現可能很適合一個共享內存的小機器,某個實現可能是在NUMA多處理器的環境下,也可能是在一個大的網絡連接的集群的機器中。

NUMA Non-uniform memory access

非統一內存訪問架構是一種為多處理器的電腦設計的內存架構,內存訪問時間取決於內存相對於處理器的位置。在NUMA下,處理器訪問它自己的本地內存的速度比非本地內存快一些。 非統一內存訪問架構的特點是:被共享的內存物理上是分佈式的,所有這些內存的集合就是全局地址空間

這節描述的是 Google 雲計算環境下廣泛使用的,下面是 Google 的配置:

  1. 機器,都是典型的Linux系統,運行在基於x86的雙處理器上,每台機器 2-4GB 內存
  2. 網絡,使用商品網絡硬件,在機器級別通常為100Mb/s或1Gb/s,但平均平均對分帶寬要小得多
  3. 集群中有上百或者上千個機器,所以機器故障出現很正常
  4. 存儲,使用廉價的 IDE 硬盤直接保存每個機器自己的數據,開發出的分佈式文件系統來管理這些磁盤上的文件。文件系統使用複製來在不可靠的硬件上提供可用性和可靠性。
  5. 用戶通過一個任務調度系統提交任務。每個工作包含一系列的任務,使用任務調度器來分配到集群中可用的機器上

3.1 Execution Overview

Map 調用分佈在多個機器上,自動將輸入數據分配成 M 組,輸入的分割可以并行的發生在不同的機器上。 Reduce 調用也是分佈式的,通過將中間值的key使用一個分割函數(例如:hash(key) mod R)來將任務分配到不同的機器上。分區數量 R 的取值和分區數量也是通過用戶來指定的。

下面這個圖說明了 MapReduce 執行的完整流程:

  1. 用戶程序中的 MapReduce 庫首先將輸入文件分成 M 份(每份 16MB – 64 MB),然後開始在集群中複製很多拷貝
  2. 程序中有一份拷貝是特殊的(master)。剩下的 worker 來被分配工作,有 M 個map任務和 R 個 reduce 任務來分配給不同的 worker。master 來挑選空閑的 worker 分配給他們每個一個 map task 或者 reduce task
  3. 一個被分配到 map 工作的 worker 將會從對應的分割的內容中讀取。它會解析 k/v pair 到輸入數據並且傳給到用戶定義的 map 函數。Map 函數產生的中間 key/value 數據將會被保存在內存的緩存中
  4. 緩存中的 pair 會被周期性的寫入到本地磁盤上,通並且過分割函數將該文件分成的 R 個區域。這些緩衝對的位置在本地磁盤上被傳遞迴主服務器,該主服務器負責將這些位置轉發給reduce worker。
  5. 當一個 reduce worker 被 master 告知這些存儲的區域,reduce worker 將使用遠程過程調用來從 map workers 的本地磁盤以及緩存中讀取 pair 對。當一個 reduce worker 讀取了所有的中間數據,它將會跟配相同的 key 來進行排序。排序是必需的因為通常會有很多不同的 key 映射到同一個 reduce 任務。如果中間數據太大來放到內存中排序,外排序就會被使用
  6. reduce worker 迭代排過序的中間數據,對於每個獨特的中間值 key,它會傳遞這個 key 和對應中間值到用戶定義的 reduce 函數中來處理。Reduce函數的輸出將附加到此reduce分區的最終輸出文件中
  7. 當所有的 map task 和 reduce task 都被完成之後,master 喚醒用戶程序。這個時候,MapReduce 的調用返回到用戶的代碼邏輯中

在成功的完成之後,mapreduce執行的輸出將會在 R 個輸出文件中(每個reduce task 都會被用戶指定文件的名稱)。通常來說,用戶不會將 R 個輸出文件合併成一個,而是將這個文件作為另一個 MapReduce 的輸入。或者把他們當成另外的分佈式程序的輸入

3.2 Master Data Structures

master 保存一些數據結構。對於每個 map 和 reduce 任務,它會保存狀態(idle,in-progress,completed),以及識別每個 worker machine(非空閑任務)。

master 是處於 map 任務發送到 reduce 任務中間的導管,master 會保存中間文件區域的位置。因此,對於每個完成的 map 任務, master 保存 R 個由map任務產生的中間文件的大小和位置。當 map 任務完成之後,會更新這些文件的位置和大小。這些信息將逐漸被推送到已經在工作的 reduce 任務。

3.3 Fault Tolerance

由於 MapReduce 是用來在大量機器上處理大量數據的一個庫,所以這個庫必需能夠有很好的容錯能力。

Worker Failure (工作節點故障)

master 節點周期性的 ping 每個 worker 節點。如果在一個特定的時間內沒有收到回復,那麼 master 節點就會將這個 worker 標記為失敗。完成 map 任務的 worker 節點將會被重置為 idle 空閑狀態,然後就可以被其他 worker 節點安排。相似的,如果一個節點上的 map 或者 reduce 任務在執行過程中失敗了,那麼這個任務將會被重置然後分配然後重新分配。

如果一個 map 任務的節點在完成任務之後出現故障,那麼就需要重新執行這個任務,因為這個節點變得不可訪問。但是如果是 reduce 任務完成之後節點出現故障,不需要重新執行,這是因為 reduce 任務的輸出被保存到一個全局文件系統中。

當一個 map 任務首先在節點 A 上執行之後在節點 B 上執行時,所有正在執行的 reduce 節點將會被告知這次重新執行過程。所有還沒有從 worker A 讀取數據的 reduce 任務將會 worker B 讀取。

MapReduce 可以處理大規模的 worker 節點故障。例如在一次 MapReduce 任務中,一個由80台計算機組成的集群由於網絡問題無法訪問,MapReduce 的 master 節點只是簡單的讓那些不能正常執行任務的工作節點再次執行任務,然後繼續向前執行任務直到完成 MapReduce 操作。

Master Failure(主節點故障)

讓 master 節點周期性的上述master節點的數據結構的檢查點。如果 master task 失敗了,可以從上一個檢查點的拷貝恢復。但是如果只有一個主節點,那麼出現故障的可能性非常小,因此如果主節點出現故障,我們的當前的實現就中止了此次 MapReduce 任務。客戶端可以檢查到這種情況,然後可以選擇是否重試 MapReduce 操作。

Semantics in the Presence of Failures(語義可能存在的故障)

當用戶指定的 map 和 reduce 操作對於他們的輸入輸出都確定好了之後,分佈式的實現將會產生一個類似於線性執行過程任務執行的結果。

我們依賴於 map 和 reduce 任務的 原子性 commit 作為這個特性的保證。每個執行過程中的任務都會將把他的輸出保存到一個私有的臨時文件中。一個 reduce 任務產生一個這樣的文件,但是一個 map 任務將會產生 R 個這樣的文件。當一個 map 任務完成之後, worker 節點發送包含這 R 個文件名的消息到 master 節點。如果這個節點已經接收到完成的消息,那麼將會忽略這個消息,否則將會把這些文件名保存到 master 節點的數據結構中。

當一個 reduce 任務完成的時候,reduce worker 將會原子性的將它的臨時文件重命名成一個輸出文件。如果有以個 reduce 任務在多個機器上同時完成,那麼這個重命名的操作將會對於一個輸出文件多次執行。我們依賴於基礎文件系統提供的原子重命名操作,以確保最終文件系統狀態僅包含一次執行reduce任務所產生的數據。

map 和 reduce 操作絕大部分都是確定性的,事實上我們的語義將會和線性順序執行的程序的結果一致,這樣很容易分析程序的行為。當 map/reduce 操作是不確定的時候,我們提供弱化但是可信的語義。例如在一個不確定的語義中,一個特定 reduce 任務的輸出和這個任務順序執行的結果一致。然而,用於不同reduce任務R2的輸出可以對應於由不確定性程序的不同順序執行所產生的用於R2的輸出。(這裏保留疑問,沒有太懂什麼意思,指的是有可能是線性結果一致的意思嗎?)

考慮有一個 map 任務 M,和兩個 reduce 任務 R1,R2,e(Ri)是 Ri提交的結果,弱一點的語義指的是,e(R1) 可能讀取的 M 的一個執行的結果而 e(R2) 可能讀取的是 R 執行輸出的另外一個結果。

3.4 Locality

網絡帶寬是在雲計算環境中比較稀缺的資源(盡量少用)。通過將輸入文件(由GFS保管)保存到本地磁盤上來減少網絡帶寬的使用。GFS 將每個文件分成 64 MB的塊,然後將每塊保存幾個副本(通常為3份)在不同的機器上。MapReduce 盡量將這些位置信息保存下來然後盡量將含有某個文件主機的任務分配給它,這樣就可以減少網絡的傳遞使用。如果失敗,那麼將會嘗試從靠近輸入數據的一個副本主機去啟動這個任務。當在一個集群上執行大型的 MapReduce 操作的時候,輸入數據一般都是本地讀取,減少網絡帶寬的使用。

3.5 Task Granularity

我們將一個 map 任務分成 M 塊,然後 reduce 會處理最後輸出成 R 塊。

理想情況下,M 和 R 應該遠遠大於集群中的 worker 節點數量。讓每個節點執行不同的任務將會有利於動態的負載均衡,同時會加速當一個 worker 節點故障之後的恢復,map 任務完成之後可以分配到所有的其他節點上。

R 和 M 在實現過程中會有邊界,因為 schedule 決策需要 O(M + R)的時間,保存這個信息到內存中需要 O(M*R)的複雜度(常數內存很小)。

R 通常是由用戶指定的,這是由於每個 reduce 任務的輸出將會輸出到單獨的文件中。在實際中,通常 M 被選擇到每個單獨的任務輸入數據將會是 16MB ~ 64MB,讓 R 要比我們使用的機器的數目的小几倍。

例如,M =20,000,R = 5,000,worker machines = 2000

3.6 Backup Tasks

讓整個 MapReduce 任務時間延長的原因主要有 “拖延者”:某一個機器在 map/reduce 任務上花費了太多的時間。導致這個 “拖延者” 的原因可能有很多,比如一塊讀寫速度超級慢的硬盤 1MB/s(其他的是 30MB/s)比如集群在這個機器上也分配了其他任務,這些任務競爭使用 CPU、硬盤網絡等等。我們最近遇到的一個問題是機器初始化代碼中的一個錯誤,該錯誤導致禁用了處理器緩存:受影響機器的計算速度降低了一百倍。

4 Refinements

儘管上面說的夠用了,但是我們還是找到一些可以優化的點。

4.1 Partitioning Function

用戶將會指定輸出文件的數量 R。輸入數據將會根據中間值來把這些數據分區。一個默認的分區函數就是 hash函數(hash(key) mod R) 。通常情況下這樣很好,但是在某些情況下不是很好。例如,輸出數據是 URL key,我們希望具有相同主機 hostname 的 URL 在一起,這樣,MapReduce 提供了用戶自己制定 分區函數的方式,例如可以寫為(hash(Hostname(urlkey))),這樣具有相同的 hostname 的URL將會在一個相同的輸出文件中。

4.2 Ordering Guarantees

我們保證了在某個給定的分區中,中間值的 k/v pair 將會按照增序排列,這樣在某些需要有序的場景下是很有用的。

4.3 Combiner Function

在某些情況下,最終的輸出文件reduce是需要根據中間值來合併的。例如在2.1的 word count 中,需要統計每個單詞的數目,我們輸出的是 <word, 1> 這樣的形式。這些pair都需要通過網絡來進行發送到 reduce 工作節點,我們提供了一個 combiner 函數,讓用戶可以在發送數據之前執行的函數,也就是說在本地先合併,然後再發送到網絡中去。

在每個執行 map task 的機器都會執行 conbine 函數。

combine 和 reduce 的唯一區別:

  • combine 輸出是到中間值文件中
  • reduce 輸出到一個最終的輸出文件中

4.4 Input and Output Types

MapReduce 庫提供了幾種輸入的類型。

例如,在 “text” 模式下輸入將每一個行當作 k/v pair,這行的偏移量當作 key,這行的內容當作 value。

用戶可以通過實現 reader 接口在實現自己的輸入類型,儘管大多數用戶都只使用預定義的類型。

reader 不一定要從文件中讀取數據,也可以從數據庫中讀取數據,或者從內存中的某個的某個數據結構中獲取數據。

同樣的,輸出也可以自定義。

4.5 Side-effects

在某些情況下,用戶需要產生一些額外的輸出文件在reduce 的輸出結果中。我們依靠這個應用程序自己的編寫者來使此類副作用成為原子和冪等的。通常應用程序將會寫入一個臨時文件中,然後當它完成的時候將會原子性的重命名這個文件。

我們不提供原子性的兩節點提交由一個任務產生的多個輸出文件。因此,產生多個輸出的結果的任務應該是確定性的。

4.6 Skipping Bad Records

在處理大量數據的時候,由於用戶的 map/reduce 函數的錯誤在處理某些數據的時候產生bug,這個時候可以選擇跳過這些 bug。有的時候我們可以查找到bug所在的地方,但是有的時候我們找不到bug,因為可能是第三方的庫導致的錯誤,我們提供了一種可選的執行模式來跳過這些可能出現錯誤的記錄。

每個 worker 進程都會有一個監聽段錯誤和總線錯誤的處理器。在執行用戶的 map/reduce 函數之前,mapreduce 將會在一個全局變量中保存順序編號。如果用戶代碼產生了一個 signal,那麼就會發送一個 UDP包到 MapReduce 的 master 節點上。當 master 節點發現在某個幾點上出現了很多次故障的時候,之後就會跳過這個記錄。

4.7 Local Execution

在 Map/Reduce 中debug需要一些 trick,因為在分佈式系統中執行可能是在幾千台機器中,工作分配也是動態的。MapReduce 提供了一個另外的本地MapReduce 的本地實現(順序執行),這樣就可以在本地來進行 debug了。

4.8 Status Information

master 節點通過 HTTP 服務器提供一個显示當前狀態的網頁。這個界面显示了多少任務完成了,多少任務還在執行,中間數據有多少字節等,還包含了錯誤的標準輸出文件的鏈接等,用戶可以通過這個界面預估還有多久可以完成任務。當執行很慢的時候,可以通過這個界面來查找原因。

另外,top-level 的狀態信息還會显示哪些 worker 節點有故障,哪些任務失敗了。

4.9 Counters

提供一個全局的計數器來統計某些數據,例如統計大寫單詞的出現次數。

 Counter* uppercase;
  uppercase = GetCounter("uppercase");
  map(String name, String contents):
    for each word w in contents:
			if (IsCapitalized(w)): 
				uppercase->Increment(); 
			EmitIntermediate(w, "1");

計數器的值會從每個節點周期性的發送到master節點,主節點統計計數器的值並且在 狀態頁面显示。

有些值MapReduce 會自己去統計。

計數器功能對於完整性檢查MapReduce操作的行為很有用。例如,在某些MapReduce操作中,用戶代碼可能想要確保所生成的輸出對的數量完全等於所處理的輸入對的數量,或者所處理的德語文檔的比例在該比例之內。

Others

shuffle:將所有具有相同 key 的value 發送個單個的 reduce 進程,在網絡上傳輸數據,是MapReduce代價最大的部分

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

【其他文章推薦】

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

使用json-server與Mockjs搭建模擬服務

為什麼使用

在項目開發中,常常需要邊寫前端頁面邊寫後端接口,但是後端接口服務往往是滯後於前端開發的,或者是不能及時提供的。出於前端開發的迅速和便捷去考慮,我們可以根據後端接口數據結構去模擬(mock)數據從而實現前端的獨立開發。
JsonServer 主要的作用就是搭建本地的數據接口,創建json文件,便於調試調用
Mockjs 主要的作用就是生成隨機數據,支持生成隨機的文本、数字、布爾值、日期、郵箱、鏈接、圖片、顏色等

如何使用

以下操作需要node.js環境

1.創建文件夾

選擇自己喜歡的位置創建一個文件夾,比如E:/mock

2.安裝json-server

進入E:/mock

npm install json-server -g 

安裝完執行 json-server -h,若安裝成功則會显示選項

Options:
–config, -c Path to config file [default: “json-server.json”]
–port, -p Set port [default: 3000]
–host, -H Set host [default: “localhost”]
–watch, -w Watch file(s) [boolean]
–routes, -r Path to routes file
–middlewares, -m Paths to middleware files [array]
–static, -s Set static files directory
–read-only, –ro Allow only GET requests [boolean]
–no-cors, –nc Disable Cross-Origin Resource Sharing [boolean]
–no-gzip, –ng Disable GZIP Content-Encoding [boolean]
–snapshots, -S Set snapshots directory [default: “.”]
–delay, -d Add delay to responses (ms)
–id, -i Set database id property (e.g. _id) [default: “id”]
–foreignKeySuffix, –fks Set foreign key suffix (e.g. _id as in post_id)
[default: “Id”]
–quiet, -q Suppress log messages from output [boolean]
–help, -h Show help [boolean]
–version, -v Show version number [boolean]

3.使用json-server
  • 創建json文件,如db.json,文件內容如下
{ "posts": [ { "id": 1, "title": "json-server", "author": "typicode" } ], "comments": [ { "id": 1, "body": "some comment", "postId": 1 } ], "profile": { "name": "typicode" } } 
  • 啟動json-server,cmd執行
json-server --watch db.json 

看到如下內容

 

1574216715(1).jpg


則運行成功,直接訪問圖中显示的三個地址可得結果,如


 

1574216903(1).jpg


至此,簡單的json-server服務已經搭建成功了,後續如果有更多需求,如跨域、參數查詢、路由這些,請參考json-server的github

 

4.安裝Mockjs

進入E:/mock

npm install mockjs --save 
5.使用Mockjs
  • 創建js文件,如news.js,文件內容如下
let Mock=require('mockjs'); let Random=Mock.Random; module.exports=()=>{ let data={ news:[] }; let images=[1,2,3].map(x=>Random.image('120x60',Random.color(),Random.word(2,6))); for(let i=1;i<=100;i++){ let content=Random.cparagraph(0,10); data.news.push({ id:i, title:Random.cword(8,20), desc:content.substr(0,40), tag:Random.cword(2,6), views:Random.integer(100,5000), images:images.slice(0,Random.integer(1,3)) }) } return data } 
  • 啟動json-server,cmd執行
json-server --watch news.js 

訪問結果(部分)


 
 

完成了以上的搭建與驗證過程后,你就可以開始使用json-server與Mockjs來繼續構建模擬服務器了,來滿足自己的各種需要

Tips:

1、也可以使用json-server db.json ,使用“json-server –watch db.json”命令可以實時監測db.json的變化;如果沒有 — watch 命令,即使db.json已經發生了改變,重新發請求,仍然會返回原先的mock data,返回狀態碼304,認為沒有變化。

2、同時,我們可以發送 POST、PUT、PATCH和DELETE請求,相應的結果會通過lowdb自動保存到db.json。關於POST、PUT等相關請求的發送。

3、我們的request body應該是一個json對象,比如{“name”:”Lynn”};

4、POST、PUT、PATCH請求頭中要包含Content-Type: application/json;

5、id的值是自動生成且不易變的。PUT請求和PATCH請求中自帶的id會被忽略。

 

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

【其他文章推薦】

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

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

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

※超省錢租車方案

springboot_自動配置原理

目錄

  • 1.1 @SpringBootApplication
  • 2.1 @EnableAutoConfiguration
    • 2.1.1 @AutoConfigurationPackage
    • 2.1.2 @Import({Registrar.class})
  • 3.1 以HttpEncodingAutoConfiguration為例

springboot啥都不難,總所周知spring全家桶系列難就難在理解源碼。。。。。。。

今天結合網上資料,自己總結了一下springboot的自動配置原理。

我現在使用的springboot版本為2.3.1.不同版本的springboot在源碼上有差別!但大體一致。

管他三七二十一先打個斷點再說:

1.1 @SpringBootApplication

這個註解點進去我們可以看到:

這裏面主要關注兩個東西:

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
    第一個註解點進去:

    可以看到這個@SpringBootConfiguration本質就是一個@Configuration,標註在某個類上,表示這是一個Spring Boot的配置類。
    第二個註解@EnableAutoConfiguration: 開啟自動配置類,SpringBoot的精華所在。(最重要的就是這個註解)

2.1 @EnableAutoConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

兩個比較重要的註解:

  • @AutoConfigurationPackage:自動配置包。
  • @Import({AutoConfigurationImportSelector.class}):導入自動配置的組件。

2.1.1 @AutoConfigurationPackage

點進去瞅瞅:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}

發現這裡有導入Regitstrar類:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
        }
    }

new PackageImport(metadata).getPackageName(),它其實返回了當前主程序類的 **同級以及子級 ** 的包組件。

什麼意思呢?

我們來看這樣一個目錄:

bean1和我們的springboot啟動類位於同一個包下,二bean2不是位於我們啟動類的同級目錄或者子級目錄,那麼我們啟動的時候bean2是不會被加載到的!所以你項目的一切需要加入容器的類必須放在啟動類的同級包下或者它的子級目錄中。

2.1.2 @Import({Registrar.class})

AutoConfigurationImportSelector有一個方法為:selectImports。

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

它首先回去檢查是否開啟了自動配置類,然後才回去加載註解數據 this.getAutoConfigurationEntry(annotationMetadata);
那麼這個annotationMetadata在哪兒?
來看下面一行代碼:

  protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
再點進去我們會發現這一行代碼:
Enumeration urls = classLoader != null ? classLoader.getResources(“META-INF/spring.factories”) : ClassLoader.getSystemResources(“META-INF/spring.factories”);

它其實是去加載 public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;外部文件。這個外部文件,有很多自動配置的類。如下:

spring.factories文件由一組一組的key=value的形式,其中一個key是EnableAutoConfiguration類的全類名,而它的value是一個xxxxAutoConfiguration的類名的列表,這些類名以逗號分隔。
springboot項目啟動時,@SpringBootApplication用在啟動類在SpringApplication.run(…)的內部就會執行selectImports()方法,找到所有JavaConfig自動配置類的全限定名對應的class,然後將所有自動配置類加載到Spring容器中。

3.1 以HttpEncodingAutoConfiguration為例

@Configuration(
    proxyBeanMethods = false
)    //表示是一個配置類,可以給容器中添加組件
@EnableConfigurationProperties({ServerProperties.class})// 啟用ConfigurationProperties功能
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class})
@ConditionalOnProperty(
    prefix = "server.servlet.encoding",
    value = {"enabled"},
    matchIfMissing = true
)

@EnableConfigurationProperties({ServerProperties.class})// 啟用ConfigurationProperties功能
ServerProperties.class:

@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
)

@ConditionalOnWebApplication :spring底層@Conditional註解,根據不同的條件進行判斷,如果滿足條件整個配置類才會生效。

總結:
1.springboot會自動加載大量的自動配置類。
2.只要我們要用的組件有,我們就不需要再去配置
3.給容器添加組件的時候。會從properties類中獲取某些屬性。我們就可以在配置文件中指定這些屬性。
xxxxxAutoConfiguration:自動配置類

給容器中添加屬性:
xxxxProperties:封裝配置文件中的相關屬性。

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

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

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

※超省錢租車方案

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

Java 多線程基礎(十一)線程優先級和守護線程

 Java 多線程基礎(十一)線程優先級和守護線程

一、線程優先級

Java 提供了一個線程調度器來監控程序啟動後進去就緒狀態的所有線程。線程調度器通過線程的優先級來決定調度哪些線程執行。一般來說,Java的線程調度器採用時間片輪轉算法使多個線程輪轉獲得CPU的時間片。然而根據實際情況,每個線程的重要程序也不相同,有時候我們想讓一些線程優先執行,那麼我們可以將他的優先級調高一下,這樣它們獲得的時間片會多一些。

多個線程處於就緒狀態時,若這些線程的優先級相同,則線程調度器會按時間片輪轉方式或獨佔方式來分配線程的執行時間。

java 中的線程優先級的範圍是1~10,默認的優先級是5。“高優先級線程”會優先於“低優先級線程”執行。

Java中線程優先級分為三個級別:

  • 低優先級:1~4,其中類變量 Thread.MIN_PRORITY 最低,數值為1;
  • 默認優先級:如果一個線程沒有指定優先級,默認優先級為5,由類變量 Thread.NORM_PRORITY表示;
  • 高優先級:6~10,類變量 Thread.MAX_PRORITY 最高,數值為10。

注意:具有相同優先級的多個線程,若它們都為高優先級Thread.MAX_PRORITY,則每個線程都是獨佔式的,也就是這些線程將被順序執行;若它們優先級不是高優先級,則這些線程將被同時執行,可以說是無序執行。

java 中有兩種線程:用戶線程和守護線程。可以通過 isDaemon() 方法來區別它們:如果返回 false,則說明該線程是“用戶線程”;否則就是“守護線程”。
用戶線程一般用戶執行用戶級任務,而守護線程也就是“後台線程”,一般用來執行後台任務。需要注意的是:Java虛擬機在“用戶線程”都結束後會後退出。

JDK中對用戶線程與守護線程的解釋:

每個線程都有一個優先級。“高優先級線程”會優先於“低優先級線程”執行。每個線程都可以被標記為一個守護進程或非守護進程。在一些運行的主線程中創建新的子線程時,
子線程的優先級被設置為等於“創建它的主線程的優先級”,當且僅當“創建它的主線程是守護線程”時“子線程才會是守護線程”。
當Java虛擬機啟動時,通常有一個單一的非守護線程(該線程通過是通過main()方法啟動)。JVM會一直運行直到下面的任意一個條件發生,JVM就會終止運行:
①、調用了exit()方法,並且exit()有權限被正常執行。 ②、所有的“非守護線程”都死了(即JVM中僅僅只有“守護線程”)。
每一個線程都被標記為“守護線程”或“用戶線程”。當只有守護線程運行時,JVM會自動退出。
 

二、線程優先級示例

public class Demo01 {
    private static Object obj = new Object();
    public static void main(String[] args) {
        
        Thread t1 = new ThreadA("t1");
        Thread t2 = new ThreadA("t2");
        
        t1.setPriority(1); // 設置優先級為1
        t2.setPriority(10);// 設置優先級為10
        
        t1.start();
        t2.start();
    }
}
class ThreadA extends Thread{
    public ThreadA(String name) {
        super(name);
    }
    public void run() {
        for(int i = 0;i < 3;i++)
            System.out.println(Thread.currentThread().getName() + " [ " 
                    + Thread.currentThread().getPriority() + " ] loop " + i);  
    }
}
// 運行結果
t1 [ 1 ] loop 0
t2 [ 10 ] loop 0
t2 [ 10 ] loop 1
t2 [ 10 ] loop 2
t1 [ 1 ] loop 1
t1 [ 1 ] loop 2

說明:

①、主線程main的優先級是5。
②、t1的優先級被設為1,而t2的優先級被設為10。cpu在執行t1和t2的時候,根據時間片輪循調度,所以能夠併發執行。

三、守護線程示例

public class Demo {
    public static void main(String[] args) {

        System.out.println(Thread.currentThread().getName()
                +" [ isDaemon = "+Thread.currentThread().isDaemon()+ " ]");
        Thread t1=new ThreadA("t1");    
        Thread t2=new MyDaemon("t2");    
        t2.setDaemon(true);// 設置t2為守護線程
        t1.start();                        
        t2.start();                        
    }
}
class ThreadA extends Thread{
    public ThreadA(String name) {
        super(name);
    }

    public void run(){
        try {
            for (int i=0; i<5; i++) {
                Thread.sleep(3);
                System.out.println(this.getName() +"[ isDaemon = "+this.isDaemon()+ " ] " + "loop " + i);
            }
        } catch (InterruptedException e) {
        }
    }
};

class MyDaemon extends Thread{
    public MyDaemon(String name) {
        super(name);
    }

    public void run(){
        try {
            for (int i=0; i<10000; i++) {
                Thread.sleep(1);
                System.out.println(this.getName() +"[ isDaemon = " + this.isDaemon() +  " ] " +"loop "+i);
            }
        } catch (InterruptedException e) {
        }
    }
}
// 運行結果
main [ isDaemon = false ]
t2[ isDaemon = true ] loop 0
t2[ isDaemon = true ] loop 1
t2[ isDaemon = true ] loop 2
t1[ isDaemon = false ] loop 0
t2[ isDaemon = true ] loop 3
t2[ isDaemon = true ] loop 4
t1[ isDaemon = false ] loop 1
t2[ isDaemon = true ] loop 5
t2[ isDaemon = true ] loop 6
t2[ isDaemon = true ] loop 7
t1[ isDaemon = false ] loop 2
t2[ isDaemon = true ] loop 8
t2[ isDaemon = true ] loop 9
t2[ isDaemon = true ] loop 10
t1[ isDaemon = false ] loop 3
t2[ isDaemon = true ] loop 11
t2[ isDaemon = true ] loop 12
t2[ isDaemon = true ] loop 13
t2[ isDaemon = true ] loop 14
t1[ isDaemon = false ] loop 4
t2[ isDaemon = true ] loop 15

說明:

①、主線程main是用戶線程,它創建的子線程t1也是用戶線程。
②、t2 是守護線程。在“主線程main”和“子線程t1”(它們都是用戶線程)執行完畢,只剩t2這個守護線程的時候,JVM自動退出。

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

Linux下9種優秀的代碼比對工具推薦

大家好,我是良許。

在我們編寫代碼的時候,我們經常需要知道兩個文件之間,或者同一個文件不同版本之間有什麼差異性。在 Windows 下有個很強大的工具叫作 BeyondCompare ,那在 Linux 下需要用到什麼工具呢?

本文介紹 9 種 Linux 下常用的 9 種代碼比對工具,不僅有命令行工具,還有 GUI 界面工具,讓你輕鬆進行代碼比對。

1. diff命令

diff 命令是 Linux 下自帶的一個強大的文本比對工具,而且使用起來非常方便。對於它的使用,我之前也單獨寫過一篇文章介紹,點擊下方鏈接可以查看。

教你一招Linux下文本比對方法

diff 命令在大多數的 Linux 發行版里已經預裝了,它可以逐行比對兩個文本文件,並輸出它們的差異點。更多介紹可以直接查看它的 man 手冊。

$ man diff

但是,diff 命令雖然強大,但它的輸出結果實在是太感人了,不直觀也不清晰。於是,有大佬為了彌補這個缺點,基於 diff 開發了更強大的工具。這裏推薦兩個:colordiffwdiff

colordiff命令

colordiff 是一個 Perl 腳本工具,它的輸出結果和 diff 命令一樣,但是會給代碼着色,並且具有語法高亮功能。同時,你如果不喜歡它的默認顏色的話,還可以自定義主題。

你可以自行安裝 colordiff 到你的電腦,根據不同的發行版選擇不同的安裝命令。

$ yum install colordiff             [On CentOS/RHEL/Fedora]
$ dnf install colordiff             [On Fedora 23+ version]
$ sudo apt-get install colordiff    [On Debian/Ubuntu/Mint]

同樣,你可以使用 man 命令查看它的幫助文檔:

$ man colordiff
wdiff命令

diff 命令是逐行比較差異,而 wdiff 更變態,是逐字比較。所以如果你的文本只是修改了少數一些詞語的話,使用 wdiff 命令將更加高效。

安裝命令如下:

$ yum install wdiff             [On CentOS/RHEL/Fedora]
$ dnf install wdiff             [On Fedora 23+ version]
$ sudo apt-get install wdiff    [On Debian/Ubuntu/Mint]

更詳細內容可以查看它的 man 手冊。

$ man wdiff

2. vimdiff命令

vimdiff 等同於 vim -d 命令,即 Vim 編輯器的 diff 模式。

該命令後面通常會接兩個或多個文件名作為參數,這些文件會同時在 Vim 編輯器的分割窗口中打開,並高亮显示文件中內容有差異的部分。

它的中文主頁是:http://vimcdoc.sourceforge.net/doc/diff.html

以上介紹的兩款是 Linux 命令行的對比工具,我們再來看一些 GUI 比對工具。

3. Kompare

Kompare 是基於 diff 的一個 GUI 工具,使用者可以很方便看到文件之間的差異,並且支持合併這些差異。

Kompare 的特性有如下:

  • 支持多種 diff 格式;
  • 支持目錄之間的比對;
  • 支持讀取 diff 文件;
  • 自定義界面;
  • 創建及應用源文件的 patch 文件。

該工具的主頁為:https://www.kde.org/applications/development/kompare/

4. DiffMerge

DiffMerge 是一個跨平台的 GUI 文本比對工具,具有 Linux ,Windows ,macOS 三大平台版本。我們知道,BeyondCompare 是一款收費軟件,所以如果你們公司的版權要求比較高的話,不妨考慮一下 DiffMerge工具。

DiffMerge 具有兩大功能:1. 圖示化显示兩個文件之間的改變。包含內部行高亮和完整的編輯支持。2. 圖示化显示三個文件之間的改變。允許自動合併(當可以安全操作時)和對結果文件完全編輯控制。

它具有以下特性:

  • 支持文件夾比對;
  • 集成文件瀏覽器;
  • 高度可配置。

該工具的主頁為:https://sourcegear.com/diffmerge/

5. Meld

Meld 是一個輕量級 GUI 代碼比對工具,它支持用戶比對文件、目錄,並且高度集成版本控制軟件。但針對軟件開發人員,它的以下幾個特性尤為吸引人:

  • 執行雙向和三向差異併合並
  • 輕鬆地在差異和衝突之間導航
  • 逐個文件地比較兩個或三個目錄,显示新文件,缺失文件和更改文件
  • 支持許多版本控制系統,包括 Git,Mercurial,Bazaar 和 SVN 等。

它的官網為:http://meldmerge.org/

6. Diffuse

Diffuse 是另外一款很受歡迎的,免費,小巧,也十分簡單的 GUI 文本差異比對合併工具,它是用 Python 寫成的,具有兩個主要功能:文件比對及版本控制,允許文件編輯、合併,並且輸出兩個文件的差異點。

你可以使用它查看文本比對小結,使用鼠標選擇文件里的某行進行編輯。它的其它特性包括:

  • 語法高亮
  • 快捷鍵便於文本導航
  • 無限次撤銷
  • 支持 unicode 編碼文件
  • 支持許多版本控制系統,包括 Git,Mercurial,Bazaar 和 SVN 等。

它的官網為:http://diffuse.sourceforge.net/

7. XXdiff

XXdiff 是一款免費、強大的文件及文件夾差異比對及合併工具,它可以運行在很多類 Unix 系統上。不過它有個限制就是它不支持 unicode 文件,也沒法辦法直接編輯文件。

它具有以下特性:

  • 遞歸對比文件及文件夾
  • 高亮显示差異點
  • 合併差異點,導出結果
  • 支持外部 diff 工具,比如:GNU diff,SIG diff ,Cleareddiff ,以及其它更多工具
  • 支持腳本拓展

8. KDiff3

KDiff3 是另外一種很強大的跨平台差異比對及合併工具,它是由 KDevelop 開發而成,可以在所有類 Unix 平台上運行,包括 Linux ,Mac OS ,Windows 等。

它可以比對或合併兩到三個文件或目錄,具有以下特性:

  • 可以逐句、逐字對比差異
  • 支持自動合併
  • 內置編輯器,可以手動解決衝突
  • 支持 unicode ,UTF-8 等各種編碼格式
  • 支持打印差異

它的官網為: http://kdiff3.sourceforge.net/

9. TkDiff

TkDiff 是另外一種跨平台,易於使用的 GUI 文本比對工具,可以運行在 Linux ,Windows 及 MacOS 系統上。它同樣提供一個左右分開的界面,用於查看對比的兩個文件。

但是,它也有一些其它文本對比工具沒有的功能,比如差異書籤,以及一個便於快速定位導航差異點的導航圖。

它的官網為:https://sourceforge.net/projects/tkdiff/

公眾號:良許Linux

有收穫?希望老鐵們來個三連擊,給更多的人看到這篇文章

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

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

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

.NET Core請求控制器Action方法正確匹配,但為何404?

前言

有些時候我們會發現方法名稱都正確匹配,但就是找不到對應請求接口,所以本文我們來深入了解下何時會出現接口請求404的情況。

匹配控制器Action方法(404)

首先我們創建一個web api應用程序,我們給出如下示例控制器代碼

[ApiController]
[Route("[controller]/[action]")]
public class WeatherController : ControllerBase
{
    [HttpGet]
    string Get()
    {
        return "Hello World";
    }
}

 

當我們進行如上請求時會發現接口請求不到,這是為何呢?細心的你應該可能發現了,對於請求方法是私有,而不是公共的,當我們加上public就可以請求到了接口

[HttpGet("get")]
public string Get()
{
    return "Hello World";
}

匹配控制器Action方法本質

經過如上示例,那麼對於Action方法的到底要滿足怎樣的定義才能夠不至於請求不到呢?接下來我們看看源碼怎麼講。我們找到DefaultApplicationModelProvider類,在此類中有一個OnProvidersExecuting方法用來構建控制器和Action方法模型,當我們構建完畢所有滿足條件的控制器模型后,緊接着勢必會遍歷控制器模型去獲取對應控制器模型下的Action方法,這裏只截取獲取Action方法片段,源碼如下:

foreach (var controllerType in context.ControllerTypes)
{    
    //獲取控制器模型下的Action方法
    foreach (var methodInfo in controllerType.AsType().GetMethods())
    {
        var actionModel = CreateActionModel(controllerType, methodInfo);
        if (actionModel == null)
        {
            continue;
        }

        actionModel.Controller = controllerModel;
        controllerModel.Actions.Add(actionModel);    
    }
}

上述紅色標記則是創建Action模型的重點,我們繼續往下看到底滿足哪些條件才創建Action模型呢?

protected virtual ActionModel CreateActionModel(TypeInfo typeInfo, MethodInfo methodInfo)
{
    if (typeInfo == null)
    {
        throw new ArgumentNullException(nameof(typeInfo));
    }

    if (methodInfo == null)
    {
        throw new ArgumentNullException(nameof(methodInfo));
    }

    if (!IsAction(typeInfo, methodInfo))
    {
        return null;
    }    
    ......    
}

到了這個方法裏面,我們找到了如何確定一個方法為Action方法的源頭,由於該方法有點長,這裏我採用文字敘述來作為判斷邏輯,如下:

protected virtual bool IsAction(TypeInfo typeInfo, MethodInfo methodInfo)
{
    //如果有屬性訪問器(無效)

    //如果有NonAction特性標識無效)

    //如果重寫Equals(Object), GetHashCode()方法(無效)

    //如果實現Dispose方法(無效)

    //如果是靜態方法(無效)

    //如果是抽象方法(無效)

    //如果是構造函數(無效)

    //如果是泛型方法(無效)

    //必須為公共方法
    return methodInfo.IsPublic;
}

如上是從方法定義的角度來過濾而獲取Action方法,除此之外,我們請求方法的名稱還可以自定義,比如通過路由、ActionName特性指定,那麼這二者是否存在優先級呢?比如如下示例:

[ApiController]
[Route("[controller]/[action]")]
public class WeatherController : ControllerBase
{
    [HttpGet]
    [ActionName("get1")]
    public string get()
    {
        var routeValue = HttpContext.Request.RouteValues.FirstOrDefault();

        return routeValue.Value.ToString();
    }
}

我們可以看到此時將以ActionName特性作為方法名稱。所以在上述過濾方法定義后開始構建方法模型,在此之後還會再做一步操作,那就是查找該方法是否通過ActionName特性標識,若存在則以ActionName特性標識給定的名稱作為請求方法名稱,否則以方法定義名稱為準,源碼如下:

var actionModel = new ActionModel(methodInfo, attributes);

AddRange(actionModel.Filters, attributes.OfType<IFilterMetadata>());

var actionName = attributes.OfType<ActionNameAttribute>().FirstOrDefault();
if (actionName?.Name != null)
{
    actionModel.ActionName = actionName.Name;
}
else
{
    actionModel.ActionName = methodInfo.Name;
}

還沒完,若是將路由特性放到Action方法上,如下,此時請求接口應該是weather/get還是weather/get1呢?

[ApiController]
public class WeatherController : ControllerBase
{
    [HttpGet]
    [Route("weather/get")]
    [ActionName("get1")]
    public string get()
    {
        var routeValue = HttpContext.Request.RouteValues.FirstOrDefault();

        return routeValue.Value.ToString();
    }
}

此時若我們以weather/get1請求將出現404,還是以路由特性模板給定為準進行請求,但最終會將路由上Action方法名稱通過ActionName特性上的名稱賦值給Action模型中的ActionName進行覆蓋,源碼如下,所以上述我們得到的action名稱為get1,,當然這麼做沒有任何實際意義。

public static void AddRouteValues(ControllerActionDescriptor actionDescriptor,ControllerModel controller,ActionModel action)
{
    foreach (var kvp in action.RouteValues)
    {
        if (!actionDescriptor.RouteValues.ContainsKey(kvp.Key))
        {
            actionDescriptor.RouteValues.Add(kvp.Key, kvp.Value);
        }
    }

    if (!actionDescriptor.RouteValues.ContainsKey("action"))
    {
        actionDescriptor.RouteValues.Add("action", action.ActionName ?? string.Empty);
    }

    if (!actionDescriptor.RouteValues.ContainsKey("controller"))
    {
        actionDescriptor.RouteValues.Add("controller", controller.ControllerName);
    }
}

總結

本文我們只是單獨針對查找Action方法名稱匹配問題做了進一步的探討,根據源碼分析,對Action方法名稱指定會做3步操作:第一,根據方法定義進行過濾篩選,第二,若方法通過AcionName特性標識則以其所給名稱為準,否則以方法名稱為準,最終賦值給ActionModel上的ActionName屬性,第三,將ActionModel上的ActionName值賦值給路由集合中的鍵Action。

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

【其他文章推薦】

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

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

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

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

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

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

BMW 高階電動車 i3 與 i8 正式登陸

BMW Corp  21 日首度針對中國大陸消費者行銷純電動車「i3 」,讓當地剛剛萌芽的高階電動車市場競爭開始升級。   華爾街日報 22 日報導,i3 21 日在大陸正式開賣、定價約 73,000 美元。i3 是 BMW 在大陸第一款純電動車,電動車製造商特斯拉 (Tesla) 才剛在 4 月份將大陸第一台「Model S」電動車交給當地顧客。Model S 在大陸的定價約 121,000 美元。   BMW 雖然並未透露正式的銷售目標,但該公司中國分部執行長 Karsten Engel 曾在 4 月表示,BMW 希望 2014 年能在當地賣出 1,000 台左右的 i3 與 i8。BMW 目前已經在北京、上海、深圳以及瀋陽指定了 7 家經銷商。   特斯拉成長動能逐漸趨緩,得在亞洲(尤其是大陸)加把勁,才能達成 2014 年預設的銷售目標。現在 BMW 又來搶食大餅,特斯拉可得趕緊擬定對策了。  

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

【其他文章推薦】

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

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

大陸代工給鴻海?特斯拉駁斥傳言

美國電動車廠特斯拉交貨能力大幅提升,出現逾 7 成的成長,面對會否將代工業務拉回大陸時,特斯拉卻傾向冷處理,甚至還主動發出官方回應,強調「目前沒有在大陸代工生產的計畫」,看來先前外界傳出鴻海要在中國替 Tesla 代工電動車的合作計畫,恐怕還要再等一等。   特斯拉 2013 年共交付 2 萬輛 Model S,2014 年有機會上看 3.5 萬輛,市場將特斯拉交貨能力大增原因指向生產線的擴展,特斯拉美國生產基地最近又新增了一條生產線,周產能達 1,000 輛。   除了在美擴充生產線,會否將代工產線拉回大陸,也成了市場關注焦點,但對此,特斯拉卻傾向冷處理,還特別發出官方回應,強調「特斯拉現所有在華銷售的車輛都是在美國生產,再進口到中國,目前沒有在大陸代工生產的計畫。」  

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

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

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

高盛:再下60億美元資本 特斯拉就能顛覆汽車界

上周,高盛分析師派翠克•阿爾尚博(Patrick Archambault)指出,特斯拉或許需要再投入60億美元現金(尤其在2017年至2025年之間),才能實現“到2020年電動汽車的年產量達到50萬輛”的目標,從而顛覆汽車市場。   而特斯拉希望在明年年底前交付10萬輛電動汽車,其中包括Model S和即將推出的Model X。阿爾尚博預測,特斯拉要實現既定目標,到2025年為止將需要生產180萬至320萬輛電動汽車。但是,屆時如果特斯拉未能實現這些生產目標,電池工廠所生產的多餘電池將可能最終由太陽能公司SolarCity所消化。   特斯拉已經為SolarCity生產了少量的蓄電元件,超級電池工廠竣工後將滿足SolarCity部分或全部的耗電需求。馬斯克表示,兩家公司能以獨特的方式相互相容。如果更多的美國人能接受太陽能用於住宅和商業供電,特斯拉及其電池製造合作夥伴松下就能把超級電池工廠過剩產能所餘下的蓄電元件提供給SolarCity。超級電池工廠每年將為特斯拉生產50萬件電池組,因此,特斯拉不打算成為SolarCity蓄電組件的主要供應商。但如果特斯拉達不到生產目標,超級電池工廠就可能為SolarCity多出出力。

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

【其他文章推薦】

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

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

※超省錢租車方案