新能源車夯,比亞迪前三季獲利有望跳增九成

中國電動汽車商比亞迪(BYD)周日公布,2016年前九個月盈餘最高有望年增逾九成(91%),主要受惠於中國政策大力推動節能汽車。

比亞迪有股神巴菲特光環加持,上個月又獲得三星入股,可謂左右逢源。據比亞迪表示,上半年獲利來到23億人民幣,較去年同期跳增384%,若連同本季獲利,今年前三季預估將成長83-91%。

比亞迪以製造電池起家,2003年併購秦川汽車後跨足汽車市場,並為日後進軍電動車鋪路。憑藉著電池技術領先同業,比亞迪在中國新能源車領域已先立於不敗之地,近年隨著環保法規轉嚴,比亞迪生產之油電混合車與全電動車業績更是蒸蒸日上。

三星七月下旬以約30億人民幣參與比亞迪新一輪募資,亦是著眼於中國新能源車的商機。不過在三星正式入股後,巴菲特掌舵的柏克夏海瑟威公司(Berkshire Hathaway)對比亞迪的持股比例,則從原先的9.1%稀釋至8.25%。

(本文內容由授權提供)

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

【其他文章推薦】

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

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

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

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

聚甘新

IEA:今之電動車有如10年前的太陽能

英國金融時報31日報導,瑞銀(UBS)預估2021年歐洲未經補貼的純電動車整體持有成本將與傳統內燃機汽車相當、中國也可望在2025年達到這項里程碑,美國則因油價相對低廉、在可預見的未來都無法看到。國際能源署(IEA)首席經濟學家Laszlo Varro指出,電動車目前對商品(原油)市場的影響力大約就像是10年前的太陽能一樣。他說,太陽能現在已是一個規模達數百億美元的市場、擁有龐大的影響力。Varro提到,電動車需達5千萬至1億台的規模、才能取代相當於100萬桶的石油日消費量。

IEA數據顯示,2009年全球40個國家合計僅有不到6千台的電動車、去年已升至120萬台。麥格理集團全球能源策略師Vikas Dwivedi指出,沙烏地阿拉伯對電動車的長期發展存有戒心,這可能就是它為何宣布將讓沙烏地阿拉伯國家石油公司(Saudi Aramco)初次公開發行(IPO)的原因之一。

CNNMoney去年底報導,石油輸出國組織(OPEC)發表的年度「世界石油展望(World Oil Outlook)」報告顯示,2040年高達94%的使用中車輛仍將是依靠石油燃料。OPEC報告顯示,除非出現重大技術性突破,否則在可預見的未來電動車將難以大幅取得市佔率。油國組織預估2040年僅有1%的車輛銷售量是來自純電動車款。

華爾街日報6月報導,根據能源咨詢公司Wood Mackenzie發布的報告,未來20年電動車的普及可能會導致美國汽油需求縮減5-20%。美國目前平均每天的汽油使用量超過900萬桶。報告顯示,如果電動車2035年市占比重因特斯拉(Tesla)等廠商開始推出較低價車款以及續航力和便利性(註:目前大約每100英里就得充電一次)顯著突破而站上35%,美國日需求量可能會減少200萬桶。

即便電動車現在還沒有茁壯到可以侵蝕汽油需求的地步,美國依舊面臨油品供給過剩的問題。

美國石油協會(API)8月18日公布,2016年7月衡量消費者汽油需求的指標(汽油交運量)創下史上最高紀錄、日均量接近970萬桶。美國7月汽油日均產量同樣創下歷史新高、年增1.9%(月增1.3%)至1,020萬桶!

能源情報署(EIA)公布,截至2016年8月19日為止當週美國商用汽油庫存報2.327億桶、較去年同期高出8.5%。EIA公布的數據顯示,美國一般汽油每加侖零售均價8月22日報2.193美元,較一年前短少0.444美元。

(本文內容由授權提供)

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

【其他文章推薦】

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

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

※超省錢租車方案

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

網頁設計最專業,超強功能平台可客製化

聚甘新

Tesla台灣旗艦店開幕,Model S 2017年在台上市

不論是在汽車、能源還是科技界,不論發生什麼事情都在大眾目光焦點的特斯拉 (Tesla),既三月份成立臺灣分公司之後有更進一步的動作。特斯拉的臺北旗艦店在今日 (9 月1 日) 在臺北信義商圈開幕,有興趣的人可以到店體驗特斯拉房車Model S,並且預約試駕和進一步訂購,預計明年 (2017) 第一季交車。

特斯拉看中臺灣在科技產業的樞鈕地位,因此在亞太區暨中國、香港、澳門、澳洲之後,選擇臺灣,成為特斯拉全球第25 個設立的國家。而在信義商圈新光三越A11 的台灣旗艦店,則是第204 家店。選在臺灣的首善之都臺北,則是著眼信義商圈是臺北最繁盛的購物商圈,對新事物的接受度高,因此選在新光三越A11 設旗艦店,並且還跟新光三越合作,在百貨公司的地下停車場設立充電站。

電動車要能方便,必須有足夠的的充電站。Tesla香港、澳門及台灣地區區域總監范菁怡表示,目前除了設在車主自家的居家充電站之外,還會與百貨商場、飯店合作,在停車場設置目的地充電站。未來特斯拉也將在高速公路沿線設置超級充電站,服務需長距離駕駛的特斯拉車子。

由於特斯拉代表新一代的潔淨能源運用型式,對於提高都市運輸效率和降低污染有顯著效果,因此除了特斯拉的代表和合作的百貨公司代表以外,臺北市政府也由副市長林欽榮出席。另外還有負責招商相關事務的行政院全球招商及攬才聯合服務中心何怡明執行長,以及經濟部工業局黃裕峰副組長出席。林欽榮致詞時表示,臺北市仍然要繼續推動共享汽車,U-Car 計畫。

儘管目前在臺灣還只能預購特斯拉的Model S 房車車型,但是特斯拉計畫在臺引進平價車款Model 3,定價3 萬5,000 美元,預計明年引進,目前正在商品檢驗階段。

「電動車勢必是未來交通方式的主流,Tesla 一直期望扮演推動永續能源應用的角色。我們樂見台灣對環保、綠色能源的高接受度,也很高興Tesla 能成為這波改革中的一股力量。」Tesla 全球副總裁暨亞太區總裁任宇翔表示:「臺灣特殊的地理和城市發展環境,以及政府對於相關政策和補助的支持,讓Tesla 清楚看到電動車在臺灣的發展性。臺灣廠商不僅是Tesla供應鏈的合作夥伴,更在電動車的推廣上佔有重要地位。隨著Tesla 電動車在台上市與充電站的逐步擴充,Tesla 對臺灣市場的發展潛力深具信心,除了希望對合作夥伴和經濟發展帶來正面影響,更期望與臺灣一起開創電動車的新篇章。」

特斯拉之後將在臺灣設立客服,還有佈建充電網路,當特斯拉的Model S 在臺灣的道路奔馳時,超級充電站和目的地充電站在商圈、景點等地方,形成足夠的網狀分佈,維持特斯拉的輪胎不停轉動。全球有超過9,000 萬臺汽車,目前只有不到0.2% 的汽車是電動車,特斯拉推動電動車,還可以有很大的施力空間。

(首圖:設在新光三越信義A11 地下4 樓停車場的特斯拉目的地充電站,在2017 年特斯拉車子在臺灣道路奔馳之際,這類設在購物商場的充電站將會越來越多。來源:科技新報)

(本文授權轉載自《》──〈〉)

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

FB行銷專家,教你從零開始的技巧

聚甘新

.Net微服務實戰之DevOps篇,.Net微服務實戰之技術選型篇,.Net微服務實戰之技術架構分層篇

技術只是基礎

  該系列的兩篇文章《.Net微服務實戰之技術選型篇》和《.Net微服務實戰之技術架構分層篇》都是以技術角度出發描述微服務架構的實施。

  如果技術選型篇敘述的是工具,那麼架構分層篇講的就是技巧,而本篇要討論的就是原則。一直以來我會給身邊向我探討問題的人灌輸一種理念,沒有什麼技術銀彈,因為我們做的是軟件工程,提供的是問題相應的解決方案,不同類型問題的解決方案是存在着本質上的差異。

  繼續提供之前的源碼:https://github.com/SkyChenSky/Sikiro

PS:該篇文章與.Net無關,其實主要是沿用前面兩篇文章的命名,此外我認為DevOps不是簡單的工具使用,應從軟件工程角度進行出發。

什麼才是優秀的架構設計?

  曾經有好幾個同行問過我同一個問題:什麼才是優秀的架構設計?我一直信奉着兩句話一個定律

  • 架構服務於業務,技術服務於架構 
  • 康威定律(簡單理解成組織架構的設計等同於系統架構的設計)

  架構設計其實就是一種方案取捨,在有限資源里(包括但不限人力、時間)能讓團隊順利的實施技術,同時滿足業務規模的需要,我認為可以稱之為優秀的架構設計,簡單來說兩個字合適

 

架構核心要素

  核心的主要5大:性能、可用性、伸縮性、擴展性、安全性。 

  而我們所討論的微服務,選擇了擴展性,犧牲了可用性、性能,擴展性的目的就是為了快速響應需求變化降低系統耦合度提高系統模塊的復用度。而微服務的調用是通過跨進程的網絡通信的,跟進程內方法調用比無疑是慢了一個單位;原本單服務99.99%高可用,假如現在三個服務就是99.99%*99.99%*99.99%=99.97%。

  當然我們可以在基於微服務的通過引入其他技術提高可用性、伸縮性和安全,但是確保無疑是犧牲了性能,除了性能還會在團隊開發效率與運維複雜度上會受到影響。由此可見,沒有萬能技術手段,而架構其實在取捨。

  引入一種技術必定帶新的技術問題這是個必然結果,剛提到團隊開發效率運維複雜度會受到影響,那是否有辦法緩解甚至解決並提高呢?既然涉及到團隊、流程這些關鍵字那麼就應該向軟件工程方向尋找方案,合適架構實施還需要合適的開發模式進行支撐的,而風靡全球的DevOps就是不二之選。

軟件工程

   在行業盛傳的一條公式:軟件 = 軟件工程 + 程序,可想而知軟件工程的佔據多麼重要的比重。那麼什麼是軟件工程?百度是這麼解釋的:

  軟件工程是研究和應用如何以系統性的、規範化的、可定量的過程化方法去開發和維護軟件,以及如何把經過時間考驗而證明正確的管理技術和當前能夠得到的最好的技術方法結合起來的學科。它涉及到程序設計語言、數據庫、軟件開發工具、系統平台、標準、設計模式等方面。

  我自己重新總結了一個軟件工程的通俗描述,通過多人協作、有目標、有步驟、有計劃的並使用科學方法論指導開發與維護程序的這個過程。也可以用一條公式表達:軟件工程 = 工具 + 流程 + 模式。

 

軟件危機

  軟件工程的出現目的是為了解決軟件危機的。軟件危機其實是當時落後的軟件生產方式無法滿足迅速增長的計算機軟件需求,從而導致軟件開發與維護過程中出現一列的嚴重問題的現象。那麼三次軟件危機是什麼呢?我整理了個表格(詳細可以自行百度閱讀)

名稱 時間 原因 解決方案
第一次軟件危機

20世紀60年代—70年代

使用機器語言或者彙編語言在特定的機器上進行軟件的設計與編寫,引出的“抽象性”和“可移植性”的問題 高級的編程語言+瀑布開發模式
第二次軟件危機

20世紀80年代—90年代

軟件複雜性進一步升級,需要更好更好的“可組合性”(Composability)、“可延展性”(Malleability)以及“可維護性”(Maintainability) 面向對象編程語言+設計模式
第三次軟件危機 2005年至今 軟件的發展速度已經遠超於硬件的發展,體現於需求複雜度、技術複雜度、團隊協作 更好的工具、開發模式、與協作流程

 

   由上可見,軟件的快速發展直接促使了軟件工程上的進步,新的工具、新的開發與設計模式,新的協作流程也隨之而生。

開發模式的發展

  我工作多年經歷了多家公司,所經歷的有三種開發模式,瀑布、敏捷、DevOps。那麼這三種主流的開發模式也對應着三個發展階段:

瀑布開發模式

  瀑布開發模式是在第一次軟件危機1970時Winston Royce博士提出來。其思想是把項目過程劃分為主要的六個階段:需求收集、需求分析、軟件設計、程序編碼、軟件測試、運行維護。團隊劃分也通過崗位職責進行劃分:產品團隊、開發團隊、測試團隊、運維團隊。到目前為止該開發模式仍然用到做項目制的開發團隊。

  那麼其優點與劣勢也很明顯,優點是計劃明確,職責清晰,按部就班的完成就好。缺點是周期容易拖得太長,不容易調整變更,每個人只為自己職責範圍內的負責,跨部門溝通成本大(這就是為什麼我在圖裡畫了兩堵牆的原因)。我自己呆過一個瀑布模式的團隊,在項目立項后就會被項目經理調動資源成為團隊,而開發人員只會在這一次批次負責編碼與修改測試反饋的問題,基本上上線后的問題跟你無關(除非緊急嚴重的),其他的BUG也許是下一個批次的另外一個開發人員幫你填。

 敏捷開發模式

  準確的說敏捷開發是一種價值觀和原則的體現,2001年17位IT大佬想把瀑布發模式這種重量級的開發過程替換成一種更加輕量級,可惜大家都沒有達成統一意見因此把各自都認同的觀念整理出來成為敏捷宣言。

  敏捷開發其實把產品、開發、測試三種崗位職責的人緊密的聯繫了起來,由原來長周期的大目標拆解成了一個個短周期的小目標。他之所以快,不是因為寫代碼快了,而是節省了很多不必要的前置條件與返工,同時小步快跑的交付也可以提高團隊的士氣,一個長周期項目那枯燥、乏味、痛苦的過程,誰試誰知道。

  舉個例子,大家都是為公司的同個產品努力,沒有什麼合同談判可言,只要需求要求相互了解清楚並且可行就可以開幹了。寫詳細設計文檔的時間,還不如花時間多溝通下需求的核心點,想辦法設計得更容易滿足需求。短周期的交付后,產品與客戶就可以及時的查看交付效果並相應的優化與調整。(快速響應並不代表隨時隨地接受變更響應,可以統一歸到下一個迭代周期,我不贊同拍拍腦袋的變更,自己都沒清楚的功能怎麼說服客戶使用?

  

  敏捷開發的最大好處之一就是短周期的持續交付,這樣方式能在現階段的互聯網行業得到更快速的響應與市場的搶佔,同時能很好的進行技術改進與試錯。但是這種”野蠻的“方式會讓開發團隊與運維團隊形成一條鴻溝,而鴻溝的形成主要原因是運維團隊希望軟件的運作是可靠的,所以他們對資源的變動、新技術的使用尤為的小心、謹慎。

  我曾經呆過一個敏捷開發團隊,生產出了問題運維團隊會自行去修改配置,當然會越改越錯了,而且一天發布次數多了,就會起爭執。

DevOps

  DevOps可以看過是敏捷的擴展與延申,它的出現就是為了解決開發團隊與運維團隊的那條鴻溝,只要存在人工處理的方式擔心的問題總會出現,同一段程序無論執行多少次相同輸入的輸出總是一致的,但是人的處理卻不能保證,那麼使用自動化改善協作的過程,鴻溝自然就跨越了,。那麼開發團隊與運維團隊就可以為相同的目標與方向而努力。而組織架構也將演變成如下:

  

  從上圖也與開頭的康威定律做了一個很好的呼應。

 我是如何實施DevOps的?

 

技術

  這個角度是大家最樂意去關注的,在我們團隊主要使用了以下技術,腳本什麼的我就不花時間貼出來了,在我看來工具的使用,只要花點時間就能解決。

類型 名稱
持續集成/持續交付 Jenkins
源代碼管理 Gitlab
雲平台 阿里雲
軟件包管理器 私有Nuget
代碼檢查 Reshaper
容器化 Docker
分佈式鏈路跟蹤 SkyWalking
日誌系統 ES+Filebeat+kibana
系統監控 Prometheus

  原本代碼檢查想引入SonarQube代替人工檢查+Reshaper,可惜於服務器資源不足。

  對於一般的團隊,我建議優先從Gitlab+Jenkins搭建好完成CI/CD,其次把日誌系統給完善起來,這兩者完成得越早,給團隊帶來的收益就越高,後續才會有更多的時間來完善整套技術體系,這是一個良性的循環

  人延申出的就是團隊與文化,經過上面的講解大家都意識到軟件工程就是一樣多人協作的工作,只有團隊目標一致,共同負責承擔團隊的項目,願意一同與項目成長才能很好的實施DevOps。就像多匹馬拉車一樣,只有它們都有共同的目標的時候才能快速拉車到目的,如果他們一匹向東一匹西,只會讓馬車無法前行甚至四分五裂。

  在我的團隊,因為在招聘人員的時候已經進行過了篩選,所以在合作上非常的順利,當然我也經常在例會和業餘的時候都會給大家傳達思想,讓團隊成員真正的從實際意義上去理解現在的做法。

  對於已經成型的團隊來說如何去落地呢?無非三種,激勵、考核和逐步試行。如果有條件的公司可以設置獎金激勵,如果有績效考核的可以將DevOps實施納入考核目標,如果兩者都沒的,那就選取團隊里願意改變的同事進行試行,使用過後都說好的那麼更會有說服力。 

流程 

   為了落實了文化的改進與技術的使用的這個過程,我們需要科學的、有步驟、有計劃的方式完成這項工作,並且可以讓這套標準化的方式可以重複使用到其他項目上。

  在我的團隊是有產品、前端開發,後端開發、測試、運維組成的。我採用了原型模式+DevOps模式:

  •   產品人員會優先使用Axure RP工具把需求整理產出原型並與需求方確認。
  •   產品確認好的原型就是我們技術的輸入,技術拿到需求後會做一次需求評審,主要是排查需求疑惑和確認需求目標。
  •   需求明確后,由我使用Visual Project任務拆解與排期,任務會建立在我們的項目管理系統Redmine上,如果任務周期過程,我會拆分成多個可交付的短周期,一般會控制在2個星期內。
  •   接到任務后,大家就跟根據自己的任務使用PowerDesigner數據庫設計(早期是由我獨裁設計,後期團隊發展壯大了,就由業務負責人各自設計),在這個階段,如果有新的服務與新的工具庫需要部署,我就會正面與運維溝通讓他把自動化給完成。
  •   因為我們是前後端分離的,所以我們使用了Swagger減少了寫接口文檔的時間,所有任務是否完成以前端是否對接好接口為主導,前端對接好后,就會在Redmine修改自己的任務狀態並新建一個測試任務給到測試。
  •   測試會根據自己寫好的測試用例,進行對完成的任務進行場景測試,如果有BUG會在Redmine提給相應的人進行修改。一般會先由前端人員排查是否是他的交互上的BUG,如果確認是數據問題那麼就會轉給後端開發,開發人員定位BUG時,可以通過我們的SkyWalking和Kibana聯合定位問題,定位問題時間一般都在2-10分鐘。
  •   代碼合併到測試分支后就會通過Jenkins發布到測試環境,生產環境的發布是合併到生產環境後手動確認發布的。

  除此之外,每周一會有一個例會內容不限工作,也可以分享周末去哪裡娛樂了。在該迭代周期快到結束的2-3天會開一個進度會議,看看大家完成情況。因為公司沒有下午茶,所以我們自己通過玩搶紅包領到最大的兩個的請吃下午茶,最少一星期一次。

 結束

  該篇到這裏就分享結束了,也是該系列的最後一篇,我曾經認為技術與管理必須二選一,自從我成為了一個技術與團隊的負責人後,終於讓我認識到,一個優秀的技術思想還是需要一些管理手段才能很好的實施,而我們的技術管理無非就是軟件工程

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

【其他文章推薦】

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

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

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

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

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

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

聚甘新

布局之: flex(CSS3新增)

flex 基本概念

  flex布局(flex是flexible box的縮寫), 也稱為彈性盒模型 。將屬性和屬性值(display:flex; )寫在哪個標籤樣式中,誰就是 容器;它的所有子元素自動成為容器成員,稱為項目。

當一個元素的display 取值為flex,所有項目(子元素)會在一行显示;如果所有項目的尺寸之和大於容器,也不會超出父元素的寬、高度。不會換行(每個項目都會自動縮小相應的比例)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>布局之:flex</title>
    <link rel="stylesheet" href="./CSS/normalize.css">
    <style>
        section {
            width: 500px;
            height: 800px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
        }
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>
</head>
<body>
    <section>
        <div>01</div>
        <div>02</div>
        <div>03</div>
        <div>04</div>
        <div>05</div>
        <div>06</div>
    </section>
</body>
</html>

頁面效果 : 每一個項目都等比例縮小了。

 

  css代碼分為兩種: 一類是適用於容器的 (設置主軸的起始位置、換行、主軸的對齊方式、多跟軸線對齊方式);一類是適用於項目的(設置項目的位置)。

容器常用的屬性和屬性值

由於重複代碼較多,就不一 一上傳代碼了,大家可以自己動手,敲敲代碼,試試看。

一、設置主軸的起始方向  flex-direction:

默認為X軸(行):

<style>
        section {
            width: 500px;
            height: 500px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
            /* flex-direction: row; */
            /* flex-direction: row-reverse; */
            /* flex-direction: column; */
            /* flex-direction: column-reverse; */
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>

 

flex-direction:row; 默認是X軸的起始方向為開始位置 (從左到右依次擺放);
flex-direction:row-reverse; 改變X軸的起始方向為結束位置 (從右到左依次擺放);

設置主軸的起始方向為Y軸(列):

flex-direction:column; 默認是Y軸的起始方向為開始位置(從上到下依次擺放)
flex-direction:column-reverse; 改變Y軸的起始方向為結束位置(從下到上依次擺放)

二、設置項目是否換行  flex-wrap:(默認是不換行)

 <style>
        section {
            width: 400px;
            height: 400px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
            /* flex-wrap: wrap; */
            /* flex-wrap: wrap-reverse; */
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>

flex-wrap: nowrap;  默認值是不換行;(n個項目都會在一行显示.如果項目尺寸之和大於容器主軸的尺寸,則項目會自動縮小相應比列.) (參考第一個代碼 頁面結果展示)

flex-wrap: wrap; 設置換行;(超出主軸的寬,則進行換行。換行后,兩行之間會出現間距,是因為垂直方向有剩餘空間,會平均分配給第二行的上下)

flex-wrap: wrap-reverse; 倒序換行;(如果有兩行,第2行显示在前面,第一行显示在後面)

三、主軸方向的對齊方式  justify-content:

項目是一個時:

 <style>
        section {
            width: 400px;
            height: 400px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
            /* justify-content: flex-start; */
            /* justify-content: flex-end; */
            /* justify-content: center; */
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>

justify-content:flex-start; 以主軸開始方向對齊 (默認)
justify-content:flex-end; 以主軸結束方向對齊

justify-content:center; 主軸方向居中

項目是多個時:

<style>
        section {
            width: 500px;
            height: 500px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
            /* justify-content: space-between; */
            /* justify-content: space-around; */
            /* justify-content: space-evenly; */
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>

justify-content: space-between; 兩端對齊 (第一個項目在容器的起始位置,最後一個項目在容器的結束位置,中間距離相等)

 

justify-content: space-around;  分散對齊

justify-content: space-evenly;  平分剩餘空間,每個項目之間的距離相同

 

四、主軸改變為交叉軸方向的對齊方式

一根軸線:  主軸需改變為Y軸:flex-direction: column;

 

align-items: baseline; 以項目的第一行文字的基線對齊

align-items: stretch; (項目沒有給高的情況下,stretch就是默認值,如果項目沒有設置高度,就是容器的高)

 

 <style>
        section {
width: 500px; height: 500px; border: 2px solid black; margin: 50px auto; display: flex; /* 主軸需改變為Y軸 項目按列擺放 */ flex-direction: column; /* align-items: flex-start; 默認擺放方式 */ /* align-items: center; */ /* align-items: flex-end; */

} div { width: 100px; height: 100px; border: 1px solid tomato; } </style>

 

align-items: flex-start;  交叉軸從開始位置對齊
align-items: center; 交叉軸居中對齊

align-items: flex-end; 交叉軸從結束位置對齊

多根軸線: (所有項目的尺寸之和,必須大於容器的尺寸,使項目換行显示)

<style>
        section {
            width: 500px;
            height: 500px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
            flex-direction: column;
            flex-wrap: wrap;
            /* align-content: center; */
            /* align-content: flex-end; */
            /* align-content: space-between; */
            /* align-content: space-around; */
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>

 

align-content: flex-start; 交叉軸從開始位置對齊
align-content: center; 交叉軸居中對齊

align-content: flex-end; 交叉軸從結束位置對齊

align-content: space-between; 交叉軸兩端對齊

align-content: space-around; 交叉軸分散對齊

align-content: space-evenly; 交叉軸平均分配

 

項目的屬性和屬性值:

一、order 控制項目位置

order:1;
取值 : 正、負數 (默認值是 0)
值越小越靠前 值越大越靠後 。

(適用場景: 1.搜索引擎優化,提升SEO 把重要的信息在html代碼中靠前擺放,但不影響布局 2.調整項目位置)

<style>
        section {
            width: 500px;
            height: 500px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
        
        div:nth-child(4) {
            order: -1;
        }
    </style>

設置一個或多個[項目]在交叉軸的對齊方式:

 <style>
        section {
            width: 800px;
            height: 400px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
        
        div:nth-child(2) {
            align-self: center;
        }
        
        div:nth-child(3) {
            align-self: flex-end;
        }
    </style>

align-self: flex-start; 設置項目在交叉軸開始位置擺放 (默認位置)
align-self: center; 設置項目在交叉軸居中擺放

align-self: flex-end; 設置項目在交叉軸結束位置擺放

設置某一個或多個元素放大比例

  條件:所有項目的尺寸之和要小於容器的尺寸
  (沒有剩餘空間,則設置此屬性無效。)

一個元素有 flex-grow 屬性

<style>
        section {
            width: 800px;
            height: 400px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
        
        div:nth-child(2) {
            flex-grow: 1;
        }
    </style>

多個項目有flex-grow 屬性

<style>
        section {
            width: 800px;
            height: 200px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
            box-sizing: border-box;
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
            box-sizing: border-box;
        }
        
        div:nth-child(2) {
            flex-grow: 1;
        }
        
        div:nth-child(4) {
            flex-grow: 2;
        }
    </style>

效果展示

將容器的剩餘空間分成相應的flex-grow的份數,再按照每個項目的份數,分給有flex-grow屬性的項目。

 

  總之,flex使用起來特別方便,可適用於響應式布局,也可使用聖杯布局。只是屬性較多,也要多練、多實踐 ,相信你也能很快熟練使用flex的。

推薦一個小遊戲,很有趣,又能增強關於flex的使用方法 :Flexbox Froggy  http://blog.xiaoboswift.com/flexbox/#zh-cn  去幫助小青蛙回家吧~~

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

聚甘新

vs.net/vscode中使用Beetlex創建vue應用

      平時在開發Vue應用則需要安裝nodejs,vue cli等相關東西相對來說麻煩一些;如果你喜歡像vs.net/vscode創建普通項目一樣就能開發Vue項目的話那可以嘗試一下BeetleX針對Vue編寫的服務插件;只需要創建一個簡單的Console項目引用相關插件即可以構建一個單頁面的Vue項目。雖然在開發的時候需要用到Beetlex,但後期發布完全可以用在其他平台上,因為組件會針對Vue的內容最終生成一個可發布的js文件。接下來介紹一下這個插件的使用(vs.net/vscode均可)

創建項目

首先需要創建一個c#的Console項目

創建項目后需要引用兩個BeetleX的組件包,可通過Nuget安裝最新版本;分別是:BeetleX.FastHttpApi.HostingBeetleX.FastHttpApi.VueExtend;這兩個組件的作用分別是在項目中啟動HTTP服務和針對.VUE文件生成相應的javascript文件。

項目文件布局

由於是Console項目,所以需要針對相關文件存放規則,具體大概如下:

所有html,css,js和vue等文件必須存放在項目的views目錄下;對於這個目錄下用什麼子目錄存放相關文件就看自己的需求了。

服務和基礎資源配置

為了讓控制台服務作為一個HTTP服務需要做一些簡單的配置

static void Main(string[] args)
{
    var builder = new HostBuilder()
        .ConfigureServices((hostContext, services) =>
        {
            services.UseBeetlexHttp(o =>
            {
                o.Port = 80;
                o.SetDebug();
                o.LogToConsole = true;
                o.LogLevel = BeetleX.EventArgs.LogType.Info;
            },
            s =>
            {
                s.AddExts("woff;ttf;woff2");
                s.Vue().CssRewrite("/css/{1}.css").JsRewrite("/js/{1}.js");
                s.Vue().Debug();
                var resource = s.Vue().CreateWebResorce(typeof(Program).Assembly);
                resource.AddScript("vue.js", "axios.js", "beetlex4axios.js", "jquery.js", "echarts.js", "bootstrap.js", "page.js");
                resource.AddCss("bootstrap.css", "bootstrapadmin.css", "admin.css");
            },
            typeof(Program).Assembly);
        });
    builder.Build().Run();
}

前部分主要描述在那個端口開發HTTP服務,並設置相關日誌显示級別;後半部分主要是描述vue配置一些信息。這個後面會詳細描述,接下來看啟動一下服務看下日誌。

服務日誌會显示資源加載和服務端口的情況。

VUE擴展配置

前面服務啟動的時候就已經配置相關VUE的內容,這裏再詳細解說一下。

s.Vue().Debug();
s.Vue().CssRewrite("/css/{1}.css").JsRewrite("/js/{1}.js");
var resource = s.Vue().CreateWebResorce(typeof(Program).Assembly);
resource.AddScript("vue.js", "axios.js", "beetlex4axios.js", "jquery.js", "echarts.js", "bootstrap.js","page.js");
resource.AddCss("bootstrap.css", "bootstrapadmin.css", "admin.css");

Beetlex的Vue插件會管理項目的兩種資源,分別是css和javascript. 

  • Debug方法

        主要是告訴組件每次調用資源都重新從文件中生成,這樣開發都在變更相關文件的時候無須重新編譯,保存文件后刷頁面即可。此方法在Release編譯模式下並不生效。

  • CssRewrite和JsRewrite方法

         這兩個方法主要是描述通過那些路徑訪問到css和javascript資源,以上定義/css/路徑任意一文件都會得到項目中所有的css內容;/js/路徑任意文件都得到項目的javascript內容。

  • WebResource

        這個類用於描述如何收集對應的css和javascript文件;對於javascript文件來說會先打包這些基礎的文件然後再追加項目中的vue文件。打包的順序是依據定義的順序來進行。

啟動頁

在項目vue文件只是模塊文件,我們需要在根目錄下定義一個HTML文件作為訪問落地頁面,接下來看一下這頁面的定義

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <link href="/css/v1.css?group=BeetleX.AdminUI" rel="stylesheet" />
    <script src="/js/v1.js?group=BeetleX.AdminUI"></script>
    <title>BeetleX AdminUI</title>
</head>
<body>
    <div id="page">
        <main_menu @menu_resize="OnMenuResize($event)" @openwindow="OnOpenWindow($event)"></main_menu>
        <windows_bar :windows="windows" :full="full" :selectwindow="selectWindow.id" @close="OnCloseWindows($event)"></windows_bar>
        <div class="main-content" :style="{left:(full=='max'?'60px':'260px')}">
            <keep-alive>
                <component :is="selectModel" @openwindow="OnOpenWindow($event)" :token="selectWindow.data" :winsize="sizeVersion"></component>
            </keep-alive>
        </div>
        <page_footer></page_footer>
    </div>
    <script>
        var page = new Vue(pageInfo);
        page.OnOpenWindow({ id: 'home', title: '主頁', model: 'models_home' })
    </script>
</body>
</html>

頁面只是負責資源加載和VUE模塊組裝,在這裏定義的css和javascript加載組是Beetlex.AdminUI即對應剛才加載的程序集資源包:

var resource = s.Vue().CreateWebResorce(typeof(Program).Assembly);

這個落地頁的展示效果如下:

模塊定義

項目配置完成后就可以在views目錄定義自己的vue模塊,存放層次目錄沒有具體的要求可根據自己的喜好來定義存放目錄.對於vue模塊的定義和傳統的vue定義會有些差別的,模塊文件名作為對應的模塊名稱。文件內部主要有HTML和JavaScript組成,而不是像傳統那樣一個vue文件和一個js文件。下面是一個models_home.vue模塊的描述:

    <div style="width:99%;">
        <div class="row">
            <div class="col-lg-6">
                <models_panel :title="'銷售走勢'" :child="'models_monthline'" :winsize="winsize"></models_panel>
                <models_panel :title="'僱員銷售比例'" :child="`model_employeesspie`" :winsize="winsize"></models_panel>
            </div>
            <div class="col-lg-6">
                <models_panel :title="'最新訂單'" :child="`models_neworders`"></models_panel>
                <models_panel :title="'客戶訂單比例'" :child="`model_customerspie`" :winsize="winsize"></models_panel>
            </div>
        </div>
    </div>
<script>
 {
        props: ["winsize"],
        data: function () {
            return {
               
            }
        },
    }
</script>

組件支持的VUE模塊描述要相對簡單一些,沒有一些import的東西;只有HTML和一個VUE構造信息的結構體。

接下來是一個簡單的列表模塊models_employees.vue:

    <div>
        <table class="table">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Title</th>
                    <th>Region</th>
                    <th>City</th>
                    <th>Country</th>
                    <th>Address</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="item in GetEmployees.result">
                    <td><a href="javascript:void(0)" @click="OnOpen(item)">{{item.FirstName}} {{item.LastName}}</a> </td>
                    <td>{{item.Title}}</td>
                    <td>{{item.Region}}</td>
                    <td>{{item.City}}</td>
                    <td>{{item.Country}}</td>
                    <td>{{item.Address}}</td>
                </tr>
            </tbody>
        </table>
    </div>
<script>
    {
        data: function () {
            return {
                GetEmployees: new beetlexAction("/Employees", null, []),
            }
        },
        methods: {
            OnOpen: function (item) {
                this.$open('emp' + item.EmployeeID, '僱員:' + item.FirstName + ' ' + item.LastName, 'models_employeedetail', { id: item.EmployeeID });
            }
        },
        mounted: function () {
            this.GetEmployees.get();
        }
    }
</script>

發布

Beetlex原本是一個HTTP服務模塊,正常情況你把相關文件嵌入到項目發布即可在Linux或windows下運行(環境.net core 2.1或更高版本)。如果你不相基於Beetlex運行,那你可以在編譯目錄下獲取相關的javascript和css完全打包好的文件放到其他環境中部署。

代碼

如果你感興趣可以訪問 https://github.com/IKende/AdminUI   演示地址: http://adminui.beetlex.io/

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

【其他文章推薦】

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

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

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

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

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

聚甘新

循序漸進VUE+Element 前端應用開發(11)— 圖標的維護和使用,循序漸進VUE+Element 前端應用開發(5)— 表格列表頁面的查詢,列表展示和字段轉義處理,循序漸進VUE+Element 前端應用開發(9)— 界面語言國際化的處理

在VUE+Element 前端應用中,圖標是必不可少點綴界面的元素,因此整合一些常用的圖標是非常必要的,還好Element界面組件裏面提供了很多常見的圖標,不過數量不是很多,應該是300個左右吧,因此考慮擴展更多圖標,我引入了vue-awesome組件,它利用了Font Awesome的內置圖標,實現了更多圖標的整合,可以在項目中使用更多的圖標元素了,另外在本隨筆的圖標管理中,提供了對圖標名稱進行搜索,並根據圖標顏色樣式生成對應圖標的代碼,非常方便使用。

Vue-Awesome 是基於 Vue.js 的 SVG 圖標組件,內置圖標來自  Font Awesome。

本篇隨筆先來上一個圖標管理的界面效果,然後在逐一進行介紹Element內置圖標和Vue-Awesome的圖標吧。

Element圖標管理界面如下:

基於Vue-Awesome的圖標管理如下所示。

其中查詢提供了名稱進行圖標查詢,以及限制一次性展示多少個,另外提供一個自定義顏色選項,從而可以改變圖標的展示顏色。

  

1、Vue-Awesome的使用介紹

  Vue-Awesome 的 npm的安裝命令如下所示:

npm install vue-awesome

安裝方式如下所示

import Vue from 'vue'

/* 在下面兩種方式中任選一種 */

// 僅引入用到的圖標以減小打包體積
import 'vue-awesome/icons/flag'

// 或者在不關心打包體積時一次引入全部圖標
import 'vue-awesome/icons'

/* 任選一種註冊組件的方式 */

import Icon from 'vue-awesome/components/Icon'

// 全局註冊(在 `main .js` 文件中)
Vue.component('v-icon', Icon)

// 或局部註冊(在組件文件中)
export default {
  components: {
    'v-icon': Icon
  }
}

界面使用代碼如下所示,詳細Demo可以參考https://justineo.github.io/vue-awesome/demo/ 了解。

<!-- 基礎用法 -->
<v-icon name="beer"/>

<!-- 添加選項 -->
<v-icon name="sync" scale="2" spin/>
<v-icon name="comment" flip="horizontal"/>
<v-icon name="code-branch" label="Forked Repository"/>

<!-- 堆疊圖標 -->
<v-icon label="No Photos">
  <v-icon name="camera"/>
  <v-icon name="ban" scale="2" class="alert"/>
</v-icon>

Vue-Awesome圖標提供了一些prop的屬性設置,參考下面列表所示。

  • name: string

    圖標名稱。如果本組件沒有用作圖標堆棧的容器,那麼這個字段是必須的。所有合法的值都對應於圖標文件相對於 icons 目錄的路徑。請注意當你在 FontAwesome 官網查找到圖標名詞后,需要確認一下圖標集的名稱。比如,在 500px 這個圖標的詳情頁會有一個 URL 參數 style=brands,故圖標名稱就是 brands/500px

    默認情況下,只能使用 FontAwesome 的免費圖標,另外由於 solid 樣式中的圖標最多,我們將其設為了默認圖標集,所以路徑前綴可以省略。

    當傳入 null 時,整個組件將不會渲染。

  • scale: number|string

    用來調整圖標尺寸,默認值為 1

  • spin: boolean

    用來指定圖標是否需要旋轉。默認值為 false。(不能與 pulse 一同使用。)

  • pulse: boolean

    用來指定圖標是否有脈衝旋轉的效果。默認值為 false。(不能與 spin 一同使用。)

  • inverse: boolean

    為 true 時圖標顏色將被設置為 #fff。默認值為 false

  • flip: 'vertical'|'horizontal'|'both'

    用來指定圖標是否需要翻轉。

  • label: string

    當指定時會設置圖標的 aria-label

  • title: string

    為圖標設置標題。

另外,我們也可以註冊自定義圖標,如下所示。

import Icon from 'vue-awesome/components/Icon'

Icon.register({
  baidu: {
    width: 23.868,
    height: 26,
    d: 'M3.613 13.701c2.827-.608 2.442-3.986 2.357-4.725-.138-1.139-1.477-3.128-3.296-2.971C.386 6.21.052 9.515.052 9.515c-.309 1.528.74 4.793 3.561 4.186zm3.002 5.875c-.083.238-.268.846-.107 1.375.315 1.187 1.346 1.24 1.346 1.24h1.48v-3.619H7.749c-.713.213-1.057.767-1.134 1.004zM8.86 8.035c1.562 0 2.823-1.797 2.823-4.019C11.683 1.796 10.421 0 8.86 0 7.301 0 6.036 1.796 6.036 4.016c0 2.222 1.265 4.019 2.824 4.019zm6.724.265c2.087.271 3.429-1.956 3.695-3.644.272-1.686-1.074-3.644-2.552-3.98-1.48-.339-3.329 2.032-3.497 3.578-.2 1.89.271 3.778 2.354 4.046zm5.114 9.923s-3.229-2.498-5.113-5.198c-2.555-3.981-6.185-2.361-7.399-.337-1.209 2.024-3.093 3.305-3.36 3.644-.271.334-3.9 2.293-3.095 5.871.806 3.576 3.635 3.508 3.635 3.508s2.085.205 4.504-.336c2.42-.537 4.503.134 4.503.134s5.652 1.893 7.199-1.751c1.545-3.645-.874-5.535-.874-5.535zm-9.671 5.423H7.352c-1.587-.316-2.219-1.4-2.299-1.584-.078-.188-.528-1.059-.29-2.539.686-2.219 2.642-2.379 2.642-2.379h1.956V14.74l1.666.025v8.881zm6.844-.025h-4.229c-1.639-.423-1.716-1.587-1.716-1.587v-4.677l1.716-.027v4.203c.104.447.661.529.661.529h1.742v-4.705h1.825v6.264zm5.986-12.486c0-.808-.671-3.239-3.159-3.239-2.492 0-2.825 2.295-2.825 3.917 0 1.548.131 3.71 3.227 3.641 3.096-.068 2.757-3.507 2.757-4.319z'
  }
})

如果你的 SVG 文件有多個路徑或多邊形,以及/或者你想預定義一些樣式,可以用如下方式進行註冊:

路徑方式:

import Icon from 'vue-awesome/components/Icon'

Icon.register({
  webpack: {
    width: 1200,
    height: 1200,
    paths: [
      {
        style: 'fill:#8ED6FB',
        d: 'M1035.6 879.3l-418.1 236.5V931.6L878 788.3l157.6 91zm28.6-25.9V358.8l-153 88.3V765l153 88.4zm-901.5 25.9l418.1 236.5V931.6L320.3 788.3l-157.6 91zm-28.6-25.9V358.8l153 88.3V765l-153 88.4zM152 326.8L580.8 84.2v178.1L306.1 413.4l-2.1 1.2-152-87.8zm894.3 0L617.5 84.2v178.1l274.7 151.1 2.1 1.2 152-87.8z'
      },
      {
        style: 'fill:#1C78C0',
        d: 'M580.8 889.7l-257-141.3v-280l257 148.4v272.9zm36.7 0l257-141.3v-280l-257 148.4v272.9zm-18.3-283.6zM341.2 436l258-141.9 258 141.9-258 149-258-149z'
      }
    ]
  }
})

多邊形方式:

import Icon from 'vue-awesome/components/Icon'
Icon.register({
  vue: {
    width: 256,
    height: 221,
    polygons: [
      {
        style: 'fill:#41B883',
        points: '0,0 128,220.8 256,0 204.8,0 128,132.48 50.56,0 0,0'
      },
      {
        style: 'fill:#35495E',
        points: '50.56,0 128,133.12 204.8,0 157.44,0 128,51.2 97.92,0 50.56,0'
      }
    ]
  }
})

對於自定義的這些圖標,我們可以把它們一起放在一個獨立的JS文件裏面進行定義,然後全局統一加入即可。

 

 使用上和其他的圖標沒有差異,只是名稱不同而已。

    <div style="height:100px;padding:10px">
      <span>vue-Awesome 自定義圖標:(在utils/awesome-icon-customed.js中引入)</span>
      <v-icon name="baidu" scale="2" :style="iconStyle" />
      <v-icon name="vue" scale="2" />
      <v-icon name="webpack" scale="2" spin />
      <v-icon name="html5-c" scale="2" spin />
    </div>

 

2、導入Element 圖標和Vue-Awesome圖標

在我們進行頁面管理的時候,我們需要提取Element 圖標和Vue-Awesome圖標,以便能夠進行界面的展示處理。

Element圖標,我們只需要在一個JS文件裏面,包含它的名稱進去一個列表裡面即可,如下定義所示。

const elementIcons = [
  'platform-eleme', 'eleme', 'delete-solid', 'delete', ..........
]

export default elementIcons

在其中錄入對應Element的圖表名稱,移除el-icon-的前綴即可構成所需列表的每項內容。

而對於Vue-Awesome圖標,我們安裝對應的組件后,它的圖標資源肯定也一定下載下來了,我們找到對應的node_modules 目錄下的文件就可以看到了,如下截圖所示。

 可以看到它的每個圖標對應一個js文件,而且不同樣式還有不同的目錄的,我們只需要自動加入對應的文件名稱即可。

定義一個js文件,如vue-awesome-icon.js,用來獲取對應Awesome圖標名稱,如下邏輯所示 

// Vue-Awesome圖標自動加入
const req = require.context('vue-awesome/icons', true, /\.js$/)
const requireAll = requireContext => requireContext.keys()

const re = /\.\/(.*)\.js/

const vueAwesomeIcons = requireAll(req).filter((key) => {
  return key.indexOf('index.js') < 0
}).map(i => {
  return i.match(re)[1]
})

export default vueAwesomeIcons

通過 require.context 的操作以及僅需filter的數組過濾,我們就可以獲得對應的Awesome圖標名稱了。

在管理頁面裏面,我們需要引入Element和Vue-Awesome的圖標管理文件,如下所示。

import elementIcons from './element-icons' // 引入Element圖標
import vueAwesomeIcons from './vue-awesome-icons' // 引入vue-awesome圖標

然後構造頁面的data數據,如下所示,其中searchForm 是用來承載查詢條件的。

export default {
  name: 'Icons',
  data() {
    return {
      // 查詢表單
      searchForm: {
        label: '',
        pagesize: 50,
        color: '#409EFF'
      },
      // 自定義svg圖標集合
      svgIcons,
      // element圖標集合
      elementIcons,
      // vueAwesome圖標集合
      vueAwesomeIcons
    }
  },

然後增加幾個Computed函數,用來處理數據的過濾查詢等操作。

 computed: {
    iconStyle: function() {
      return { color: this.searchForm.color }
    },
    elementIconsFiltered: function() {
      const that = this
      var list = that.elementIcons.filter(item => {
        return item.indexOf(that.searchForm.label) >= 0
      })
      if (that.searchForm.pagesize > 0) {
        return list.slice(0, that.searchForm.pagesize)
      } else {
        return list
      }
    },
    vueAwesomeIconsFiltered: function() {
      const that = this
      var list = that.vueAwesomeIcons.filter(item => {
        return item.indexOf(that.searchForm.label) >= 0
      })
      if (that.searchForm.pagesize > 0) {
        return list.slice(0, that.searchForm.pagesize)
      } else {
        return list
      }
    }
  },

然後在method裏面,對常規的圖標進行生成處理即可。

  methods: {
    generateElementIconCode(symbol) {
      return `<i class="el-icon-${symbol}" style="color:${this.searchForm.color};"/>`
    },
    generateAwesomeIconCode(symbol) {
      return `<v-icon name="${symbol}" style="color:${this.searchForm.color};"/>`
    },
    handleClipboard(text, event) {
      if (text) {
        clipboard(text, event)
      }
    }
  }

對於界面的展示,我們以Vue-awesome圖標展示為例介紹,如下所示。

      <el-tab-pane label="Vue-Awesome 圖標">
        <div
          v-for="item of vueAwesomeIconsFiltered"
          :key="item"
          @click="handleClipboard(generateAwesomeIconCode(item),$event)"
        >
          <el-tooltip placement="top">
            <div slot="content">{{ generateAwesomeIconCode(item) }}</div>
            <div class="icon-item">
              <v-icon :name="item" scale="2" :style="iconStyle" />
              <span>{{ item }}</span>
            </div>
          </el-tooltip>
        </div>
      </el-tab-pane>

這樣就可以實現對應圖標的動態過濾和展示了。

 

 

列出一下前面幾篇隨筆的連接,供參考:

循序漸進VUE+Element 前端應用開發(1)— 開發環境的準備工作

循序漸進VUE+Element 前端應用開發(2)— Vuex中的API、Store和View的使用

循序漸進VUE+Element 前端應用開發(3)— 動態菜單和路由的關聯處理

循序漸進VUE+Element 前端應用開發(4)— 獲取後端數據及產品信息頁面的處理

循序漸進VUE+Element 前端應用開發(5)— 表格列表頁面的查詢,列表展示和字段轉義處理

循序漸進VUE+Element 前端應用開發(6)— 常規Element 界面組件的使用

循序漸進VUE+Element 前端應用開發(7)— 介紹一些常規的JS處理函數

循序漸進VUE+Element 前端應用開發(8)— 樹列表組件的使用

循序漸進VUE+Element 前端應用開發(9)— 界面語言國際化的處理

 

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

【其他文章推薦】

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

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

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

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

聚甘新

Spring系列.事務管理

Spring提供了一致的事務管理抽象。這個抽象是Spring最重要的抽象之一, 它有如下的優點:

  • 為不同的事務API提供一致的編程模型,如JTA、JDBC、Hibernate和MyBatis數據庫層 等;
  • 提供比大多數事務API更簡單的,易於使用的編程式事務管理API;
  • 完美整合Spring數據訪問抽象;
  • 支持Spring聲明式事務管理;

這篇博客就來介紹Spring事務管理相關的內容。

事務簡介

什麼是事務

事務(Transaction)一般是指對數據庫的一個或一組操作單元。

事務的作用

1、為數據庫操作提供了一個從失敗中恢復到正常狀態的方法,同時提供了數據庫即使在異常狀態下仍能保持一致性的方法。
2、當多個應用程序在併發訪問數據庫時,可以在這些應用程序之間提供一個隔離方法,以防止彼此的操作互相干擾。

當一個事務被提交給了DBMS(數據庫管理系統),則DBMS需要確保該事務中的所有操作都成功完成且其結果被永久保存在數據庫中,如果事務中有的操作沒有成功完成,則事務中的所有操作都需要被回滾,回到事務執行前的狀態(要麼全執行,要麼全都不執行);同時,該事務對數據庫或者其他事務的執行無影響,所有的事務都好像在獨立的運行。

事務的特點

事務具有4個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱為ACID特性。

原子性(Atomicity):事務作為一個整體被執行,包含在其中的對數據庫的操作要麼全部被執行,要麼都不執行。
一致性(Consistency):事務應確保數據庫的狀態從一個一致狀態轉變為另一個一致狀態。一致狀態的含義是數據庫中的數據應滿足完整性約束。
隔離性(Isolation):多個事務併發執行時,一個事務的執行不應影響其他事務的執行。
持久性(Durability):一個事務一旦提交,他對數據庫的修改應該永久保存在數據庫中。

事務的隔離級別

在多個事務併發操作的過程中,如果控制不好隔離級別,就有可能產生臟讀、不可重複讀或者幻讀等讀現象。數據操作過程中利用數據庫的鎖機制或者多版本併發控制機制獲取更高的隔離等級。但是,隨着數據庫隔離級別的提高,數據的併發能力也會有所下降。所以,如何在併發性和隔離性之間做一個很好的權衡就成了一個至關重要的問題。

ANSI/ISO SQL定義的標準隔離級別有四種,從高到底依次為:可序列化(Serializable)、可重複讀(Repeatable reads)、提交讀(Read committed)、未提交讀(Read uncommitted)。

  1. 讀未提交
    未提交讀(READ UNCOMMITTED)是最低的隔離級別。通過名字我們就可以知道,在這種事務隔離級別下,一個事務可以讀到另外一個事務未提交的數據。未提交讀會導致臟讀

事務在讀數據的時候並未對數據加鎖。

務在修改數據的時候只對數據增加行級共享鎖。

  1. 讀已提交
    提交讀(READ COMMITTED)也可以翻譯成讀已提交,通過名字也可以分析出,在一個事務修改數據過程中,如果事務還沒提交,其他事務不能讀該數據。讀已提交會導致不可重複讀

事務對當前被讀取的數據加 行級共享鎖(當讀到時才加鎖),一旦讀完該行,立即釋放該行級共享鎖;
事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加 行級排他鎖,直到事務結束才釋放。

  1. 可重複讀
    可重複讀能保障一個事務在事務內讀到的某條數據是一致的。但是可重複讀不能解決幻讀的問題。就是在事務還沒結束時,其他事務又插入了一條新的數據。

事務在讀取某數據的瞬間(就是開始讀取的瞬間),必須先對其加 行級共享鎖,直到事務結束才釋放;
事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加 行級排他鎖,直到事務結束才釋放

  1. 序列化
    可序列化(Serializable)是最高的隔離級別,前面提到的所有的隔離級別都無法解決的幻讀,在可序列化的隔離級別中可以解決。

事務在讀取數據時,必須先對其加 表級共享鎖 ,直到事務結束才釋放;
事務在更新數據時,必須先對其加 表級排他鎖 ,直到事務結束才釋放。

下面是臟讀、不可重複讀和幻讀的解釋。

臟讀就是指當一個事務正在訪問數據,並且對數據進行了修改,而這種修改還沒有提交(commit)到數據庫中,這時,另外一個事務也訪問這個數據,然後使用了這個數據。因為這個數據是還沒有提交的數據,那麼另外一個事務讀到的這個數據是臟數據,依據臟數據所做的操作可能是不正確的。
不可重複讀:在一個事務內,多次讀同一個數據。在這個事務還沒有結束時,另一個事務也訪問該同一數據。那麼,在第一個事務的兩次讀數據之間。由於第二個事務的修改,那麼第一個事務讀到的數據可能不一樣,這樣就發生了在一個事務內兩次讀到的數據是不一樣的,因此稱為不可重複讀,即原始讀取不可重複。
幻讀指的是一個事務在前後兩次查詢同一個範圍的時候,后一次查詢看到了前一次查詢沒有看到的數據行。幻讀專指“新插入的行”是不可重複讀(Non-repeatable reads)的一種特殊場景

Spring事務

Spring事務模型的優勢

事務可以分為本地事務和全局事務,這兩種事務都有一定程度的局限性,Spring框架的事務管理支持解決全局和本地事務模型的局限性。

1. 全局事務
全局事務可以讓你跨多個事務進行工作,比如你的事務同事包含多個關係型數據庫,也可以包含關係型數據庫和JMS事務。一般情況下都是通過JTA來實現全局事務,但是JTA一般需要具體的應用容器來支持,這就導致代碼的通用性較低。

下面舉個全局事務的列子,方便理解。

在電商網站上,在消費者點擊購買按鈕后,交易後台會進行庫存檢查、下單、減庫存、更新訂單狀態等一連串的服務調用,每一個操作對應一個獨立的服務,服務一般會有獨立的數據庫,因此會產生分佈式事務問題。分佈式事務就是一種比較常見的全局事務。

2. 本地事務

本地事務和具體的某個事務關聯,比如說JDBC事務。本地事務比較簡單,但是不能實現分佈式事務的功能。

Spring提供了統一方便的事務編程模型,可以解決上面本地事務和全局事務的局限。使用Spring的事務API進行事務管理,底層可以適應各種事務資源。

Spring事務抽象

Spring為提供統一的事務編程模型,提供相關的接口。主要接口如下:

public interface PlatformTransactionManager {

    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}

上面接口的getTransaction方法接收一個TransactionDefinition參數,返回一個TransactionStatus 值。其中TransactionStatus 可能代表一個新的事務,或者返回一個已經存在本次調用棧中的事務。(TransactionStatus 和具體的線程綁定。可以自己寫代碼測試下)

TransactionStatus接口定義如下。

public interface TransactionStatus extends SavepointManager {

    boolean isNewTransaction();

    boolean hasSavepoint();

    void setRollbackOnly();

    boolean isRollbackOnly();

    void flush();

    boolean isCompleted();
}

聲明式事務管理

使用Spring的事務管理,推薦使用聲明式事務管理。Spring的聲明式事務管理是通過Spring的AOP功能實現的。

因為平時在開發過程中都是使用註解的方式使用聲明式事務。下面就介紹註解的方式。

step1:添加@EnableTransactionManagement註解

@Configuration
@EnableTransactionManagement
@MapperScan("com.csx.demo.spring.boot.dao")
public class MyBatisConfig {

}

step2:添加@Transactional註解到接口的實現。

@Service
@Transactional(readOnly = true,rollbackFor = Exception.class)
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public int saveSysUser(SysUser user) {
        int i = sysUserMapper.insert(user);
        return i;
    }
}

使用Spring的聲明式事務就這麼簡單。

當你使用Spring的AOP方式來使用事務的話,你添加@Transactional註解的方法一定要是public的,不然事務不會生效。

假如你需要讓非public的方法生效,你需要使用AspectJ 的AOP實現。(說明:Spring的AOP功能有兩種實現方式,一種是Spring自己實現的AOP功能,主要是通過JDK動態代理或者CGLIB動態代理實現的。還有一種方式是整合AspectJ 這個第三方AOP框架實現的)

另外,@Transactional註解可以添加到接口、接口中的方法定義、類和類裏面的方法。Spring團隊建議將註解加到具體的類和方法實現上,而不是加到接口定義上(原因見下面英文描述)。當然,您可以將@Transactional註釋放在接口(或接口方法)上,但是只有在使用基於接口的代理時,才會像您期望的那樣工作。

The fact that Java annotations are not inherited from interfaces means that, if you use class-based proxies (proxy-target-class="true") or the weaving-based aspect (mode="aspectj"), the transaction settings are not recognized by the proxying and weaving infrastructure, and the object is not wrapped in a transactional proxy.

@Transactional註解的配置

@Transactional註解可以進行以下配置。

Property Type Description
value String 一個項目中可以存在多個事務管理器,這個值用於指定具體使用哪個事務管理器。
propagation enum: Propagation 設置傳播機制
isolation enum: Isolation 設置隔離級別(只有當傳播機制設置成 REQUIRED or REQUIRES_NEW時這個配置才生效)
timeout int (in seconds of granularity) 設置超時時間(以秒為單位,只有當傳播機制設置成 REQUIRED or REQUIRES_NEW時這個配置才生效)
readOnly boolean 只讀事務配置(只有當傳播機制設置成 REQUIRED or REQUIRES_NEW時這個配置才生效)
rollbackFor Array of Class objects, which must be derived from Throwable. 回滾的異常
rollbackForClassName Array of class names. The classes must be derived from Throwable.
noRollbackFor Array of Class objects, which must be derived from Throwable. 不回滾的異常
noRollbackForClassName Array of String class names, which must be derived from Throwable.

假如我們沒有配置上面的屬性,這些屬性也都是有默認值的

  • The propagation setting is PROPAGATION_REQUIRED.
  • The isolation level is ISOLATION_DEFAULT.
  • The transaction is read-write.
  • The transaction timeout defaults to the default timeout of the underlying transaction system, or to none if timeouts are not supported.
  • Any RuntimeException triggers rollback, and any checked Exception does not.(默認回滾RuntimeException )

多事務管理器

有時候項目中可能會存在多個事務管理器,比如JDBC事務,比如JMS事務。這時候我們可以通過transactionManager屬性指定。

public class TransactionalService {

    @Transactional("jdbc")
    public void setSomething(String name) { ... }

    @Transactional("jms")
    public void doSomething() { ... }
}

上面的jdbc和jms是指兩個事務管理器在Spring容器中Bean的名字。

事務的傳播機制

在TransactionDefinition這個類中定義了6中傳播機制的類型。

1. PROPAGATION_REQUIRED

2. PROPAGATION_REQUIRES_NEW

3. PROPAGATION_NESTED

只支持JDBC事務。

編程式事務管理

Spring框架提供兩種方式來進行編程式事務管理:

  • The TransactionTemplate.
  • PlatformTransactionManager 的實現。

Spring團隊推薦使用第一種方式進行編程式事務管理。

1. 使用TransactionTemplate進行事務管理

下面是使用TransactionTemplate進行事務管理的一個例子。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TxApp.class)
public class TxTest {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Test
    public void selectUserTest() {

        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        //同一個sqlSession創建的Mapper
        SysUserMapper mapper = sqlSession1.getMapper(SysUserMapper.class);
        SysUser sysUser = new SysUser();
        sysUser.setUsername("zyzl");
        sysUser.setPassword("11");

        //有返回值的操作
        transactionTemplate.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                try {
                    return mapper.insert(sysUser);
                } catch (Exception e) {
                    status.setRollbackOnly();
                    throw e;
                }
            }
        });

        //沒返回值的操作
        transactionTemplate.executeWithoutResult(new Consumer<TransactionStatus>() {
            @Override
            public void accept(TransactionStatus transactionStatus) {
                try {
                    mapper.insert(sysUser);
                } catch (Exception e) {
                    transactionStatus.setRollbackOnly();
                    throw e;
                }
            }
        });
    }

}

我們也可以通過TransactionTemplate來設定事務的隔離級別等屬性。

//設置隔離級別
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
//設置超時時間
transactionTemplate.setTimeout(30);
//設置傳播機制
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

對於不同的事務操作,如果需要不同的隔離級別和傳播機制的話,請使用不同的transactionTemplate。也就是說,你要創建不同的transactionTemplate對象來進行操作。

2. 使用PlatformTransactionManager進行事務管理

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("SomeTxName");
// 設置傳播機制
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//開啟事務
TransactionStatus status = txManager.getTransaction(def);
try {
    // execute your business logic here
}
catch (MyException ex) {
    txManager.rollback(status);
    throw ex;
}
//提交事務
txManager.commit(status);

事務綁定事件

使用@TransactionalEventListener可以在事務提交前後,回滾后等階段觸發某些操作。但是這個功能暫時還沒想到很好的使用場景。後續有需要再來用。

@Component
public class MyComponent {

    @TransactionalEventListener
    public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) {
        // ...
    }
}

重要類和接口

  • PlatformTransactionManager:事務管理器,用於獲取事務,提交回滾事務;
  • TransactionDefinition:
  • TransactionStatus:代表一個事務

進一步閱讀

Distributed transactions in Spring, with and without XA is a JavaWorld presentation in which Spring’s David Syer guides you through seven patterns for distributed transactions in Spring applications, three of them with XA and four without.(Spring實現分佈式事務的介紹)

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

【其他文章推薦】

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

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

※超省錢租車方案

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

網頁設計最專業,超強功能平台可客製化

聚甘新

.Net Core Configuration Etcd數據源

前言

    .Net Core為我們提供了一套強大的Configuration配置系統,使用簡單擴展性強。通過這套配置系統我們可以將Json、Xml、Ini等數據源加載到程序中,也可以自己擴展其他形式的存儲源。今天我們要做的就是通過自定義的方式為其擴展Etcd數據源操作。

何為Etdc

    在使用etcd之前我們先介紹一下Etcd,我相信很多同學都早有耳聞。Etcd是一款高可用、強一致的分佈式KV存儲系統,它內部採用raft協議作為一致性算法,本身也是基於GO語言開發的,最新版本為v3.4.9,具體版本下載地址可參閱官方GitHub地址。相信了解過K8S的同學對這個肯定不陌生,它是K8S的數據管理系統。官方地址為https://etcd.io/。
    在此之前,我相信大家已經了解過很多存儲系統了,Etcd到底能實現了什麼功能呢?其一用於配置中心和服務發現,再者也可以實現分佈式鎖和消息系統。它本身就是基於目錄型存儲,並且內部有一套強大的Watch機制可以監聽針對節點和數據的操作變化,每次對節點的事務操作都會有對於的版本信息。

Etcd VS Zookeeper

通過上面的介紹是不是感覺和Zookeeper有點類似呢,網上有很多很多關於Etcd和Zookeeper的對比文章,大致如下可以得到以下結論

功能 Etcd Zookeeper
分佈式鎖 有(採用節點版本號信息) 有(採用臨時節點和順序臨時節點)
watcher
一致性算法 raft zab
選舉
元數據(metadata)存儲
應用場景 Etcd Zookeeper
發布與訂閱(配置中心) 有(不限次Watch) 有(一次性觸發的,需要重新註冊Watch)
軟負載均衡
命名服務(Naming Service)
服務發現 有(基於租約節點) 有(基於臨時節點)
分佈式通知/協調
集群管理與Master選舉
分佈式鎖
分佈式隊列

說白了就是Zookeeper能幹的活,Etcd也能幹。那既然有了Zookeeper為啥還要選擇Etcd,主要基於以下原因

  • 更輕量級(Etcd基於GO語言開發,Zookeeper基於Java開發)、更易用(開箱即用)
  • 高負載下的穩定讀寫
  • 數據模型的多版本併發控制
  • 穩定的watcher功能,通知訂閱者監聽值的變化(Zookeeper基於數據的監聽是一次性的,每次監聽完成還需重新註冊)
  • 客戶端協議使用GRPC協議,支持語言更廣泛

一言以蔽之,就是不僅實現了Zookeeper的功能,還在很多方面吊打Zookeeper,這麼強大的東西忍不住都要試一試。

在.Net Core中使用Etcd

    在Nuget上可以搜索到很多.Net Core的Etcd客戶端驅動程序,我使用了下載量最多的一個名字叫dotnet-etcd的驅動包,順便找到了它在GayHub上,不好意思手滑打錯了GitHub上的項目地址,大概學習了一下基本的使用方式。其實我們結合Configuration配置這一塊,只需要兩個功能。一個是Get獲取數據,另一個是Watch節點變化(更新數據會用到)。個人認為,前期有目有邊界的學習還是非常重要的。

Configuration擴展Etcd

前面我們講到過自定義擴展Configuration是非常方便的,相信了解過Configuration相關源碼的小夥伴們已經非常熟悉了,大致總結一下分為三步:

  • 編寫IConfigurationBuilder擴展方法,我們這裏叫AddEtcd
  • 編寫實現IConfigurationSource的配置源信息類,我們這裏叫EtcdConfigurationSource
  • 編寫繼承自ConfigurationProvider的ConfigurationSource的配置數據提供類,我們這裏叫EtcdConfigurationProvider

因為微軟已經給我們提供了一部分便利,所以編寫起來還是非常的簡單的。好了,接下來我們開始編寫具體的實現代碼,重點的地方我會在代碼中註釋說明。

首先是定義擴展類EtcdConfigurationExtensions,這個類是針對IConfigurationBuilder的擴展方法,實現如下

public static class EtcdConfigurationExtensions
{
    /// <summary>
    /// AddEtcd擴展方法
    /// </summary>
    /// <param name="serverAddress">Etcd地址</param>
    /// <param name="path">讀取路徑</param>
    /// <returns></returns>
    public static IConfigurationBuilder AddEtcd(this IConfigurationBuilder builder, string serverAddress,string path)
    {
        return AddEtcd(builder, serverAddress:serverAddress, path: path,reloadOnChange: false);
    }

    /// <summary>
    /// AddEtcd擴展方法
    /// </summary>
    /// <param name="serverAddress">Etcd地址</param>
    /// <param name="path">讀取路徑</param>
    /// <param name="reloadOnChange">如果數據發送改變是否刷新</param>
    /// <returns></returns>
    public static IConfigurationBuilder AddEtcd(this IConfigurationBuilder builder, string serverAddress, string path, bool reloadOnChange)
    {
        return AddEtcd(builder,options => {
            options.Address = serverAddress;
            options.Path = path;
            options.ReloadOnChange = reloadOnChange;
        });
    }

    public static IConfigurationBuilder AddEtcd(this IConfigurationBuilder builder, Action<EtcdOptions> options)
    {
        EtcdOptions etcdOptions = new EtcdOptions();
        options.Invoke(etcdOptions);
        return builder.Add(new EtcdConfigurationSource { EtcdOptions = etcdOptions });
    }
}

這裏我還定義了一個EtcdOptions的POCO,用於承載讀取Etcd的配置屬性

public class EtcdOptions
{
    /// <summary>
    /// Etcd地址
    /// </summary>
    public string Address { get; set; }

    /// <summary>
    /// Etcd訪問用戶名
    /// </summary>
    public string UserName { get; set; }

    /// <summary>
    /// Etcd訪問密碼
    /// </summary>
    public string PassWord { get; set; }

    /// <summary>
    /// Etcd讀取路徑
    /// </summary>
    public string Path { get; set; }

    /// <summary>
    /// 數據變更是否刷新讀取
    /// </summary>
    public bool ReloadOnChange { get; set; }
}

接下來我們定義EtcdConfigurationSource,這個類非常簡單就是返回一個配置提供對象

public class EtcdConfigurationSource : IConfigurationSource
{
    public EtcdOptions EtcdOptions { get; set; }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new EtcdConfigurationProvider(EtcdOptions);
    }
}

真正的讀取操作都在EtcdConfigurationProvider里

public class EtcdConfigurationProvider : ConfigurationProvider
{
    private readonly string _path;
    private readonly bool _reloadOnChange;
    private readonly EtcdClient _etcdClient;

    public EtcdConfigurationProvider(EtcdOptions options)
    {
        //實例化EtcdClient
        _etcdClient = new EtcdClient(options.Address,username: options.UserName,password: options.PassWord);
        _path = options.Path;
        _reloadOnChange = options.ReloadOnChange;
    }

    /// <summary>
    /// 重寫加載方法
    /// </summary>
    public override void Load()
    {
        //讀取數據
        LoadData();
        //數據發生變化是否重新加載
        if (_reloadOnChange)
        {
            ReloadData();
        }
    }

    private void LoadData()
    {
        //讀取Etcd里的數據
        string result = _etcdClient.GetValAsync(_path).GetAwaiter().GetResult();
        if (string.IsNullOrEmpty(result))
        {
            return;
        }
        //轉換一下數據結構,這裏我使用的是json格式
        //讀取的數據只要賦值到Data屬性上即可,IConfiguration真正讀取的數據就是存儲到Data的字典數據
        Data = ConvertData(result);
    }

    private IDictionary<string,string> ConvertData(string result)
    {
        byte[] array = Encoding.UTF8.GetBytes(result);
        MemoryStream stream = new MemoryStream(array);
        //JsonConfigurationFileParser是將json數據轉換為Configuration可讀取的結構(複製JsonConfiguration類庫里的)
        return JsonConfigurationFileParser.Parse(stream);
    }

    private void ReloadData()
    {
        WatchRequest request = new WatchRequest()
        {
            CreateRequest = new WatchCreateRequest()
            {
                //需要轉換一個格式,因為etcd v3版本的接口都包含在grpc的定義中
                Key = ByteString.CopyFromUtf8(_path)
            }
        };
        //監聽Etcd節點變化,獲取變更數據,更新配置
        _etcdClient.Watch(request, rsp =>
        {
            if (rsp.Events.Any())
            {
                var @event = rsp.Events[0];
                //需要轉換一個格式,因為etcd v3版本的接口都包含在grpc的定義中
                Data = ConvertData(@event.Kv.Value.ToStringUtf8());
                //需要調用ConfigurationProvider的OnReload方法觸發ConfigurationReloadToken通知
                //這樣才能對使用Configuration的類發送數據變更通知
                //比如IOptionsMonitor就是通過ConfigurationReloadToken通知變更數據的
                OnReload();
            }
        });
    }
}

使用方式如下

builder.AddEtcd("http://127.0.0.1:2379", "service/mydemo", true);

順便給大家推薦一個Etcd可視化管理工具ETCD Manager,以便更好的學習Etcd。
到這裏,基本上就結束了,是不是非常簡單。主要還是Configuration本身的設計思路比較清晰,所以實現起來也不費勁。

總結

    以上代碼都已經上傳了我的GitHub,該倉庫還擴展了其他數據源的讀取比如Consul、Properties文件、Yaml文件的讀取,實現思路也都大致相似,有興趣的同學可以自行查閱。由於主要是講解實現思路,可能許多細節並未做處理還望見諒。如果有疑問或者更好的建議,歡迎評論區交流指導。

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

聚甘新

Redis 數據結構 之 SDS

SDS(simple dynamic string),簡單動態字符串。s同時它被稱為 Hacking String。hack 的地方就在 sds 保存了字符串的長度以及剩餘空間。sds 的實現在 sds.c 中。

C語言字符串使用長度為n+1的字符數組來表示長度為n的字符串,並且字符數組的最後一個元素總是空字符’\0’,這樣的方式存儲,時存在安全隱患的,並且它不能滿足效率方面的需求。

因此Redis沒有使用C原生的string而是自己構建了SDS。在Redis里,C語言字符串只用於一些無須對字符串值進行修改的地方,比如:日誌。

在Redis中,包含字符串值的鍵值對都是使用SDS實現的,除此之外,SDS還被用於AOF緩衝區、客戶端狀態的輸入緩衝區。

SDS定義

struct sdshdr{
     //字節數組
     char buf[]; 
     //buf數組中已使用字節數量
     int len;
     //buf數組中未使用字節數量
     int free;
}

如上圖所示,len表示該SDS保存了一個6字節長度(不包含結束符)的字符串,free表示該SDS還有6個字節的未使用空間,buf是一個char類型的數組 ,保存了該SDS所存儲的字符串值。

高效

相比C語言字符串,使獲取字符串長度時間複雜度降為O(1)而C原生的獲取長度為O(N) 遍歷整個數組。

安全

同時SDS杜絕緩衝區溢出,不會像C那樣造成數組數據不安全,絕對不會越界。

當需要對SDS進行修改時,API會先檢查SDS當前剩餘空間是否滿足修改之後所需的空間,如果不滿足的話API會自動將SDS的空間擴展至足夠用的空間然後才進行下一步操作,所以SDS不會出現緩衝區溢出問題。

減少內存分配

C語言字原生符串底層是使用一個n+1個字符長度的char類型數據實現的,所以每次增長或縮短一個原生字符串,程序都要對這個字符串數組進行一次內存重分配操作:

同時因為內存重分配涉及複雜的算法,並且可能需要執行系統調用,所以它通常是一個比較耗時的操作。Redis經常被用於速度要求嚴苛、數據被頻繁修改的場合,如果每次修改字符串都需要執行一次內存重分配的話,那麼對於性能會造成很大影響。

SDS 在分配了內存之後(往往空間會存在盈餘,也就是空間的預分配),然後自己通過len 和 free 來維護已使用的和未使用的內存,不再依賴系統來重新劃分,這樣能有效的提升性能。

空間預分配

用於字符串增長操作,當字符串增長時,程序會先檢查需不需要對SDS空間進行擴展,如果需要擴展,程序不僅會為SDS分配修改所必要的空間,還會為SDS分配額外的未使用空間,額外分配的未使用空間公式如下:

SDS空間 < 1MB

如果對SDS修改之後,SDS的長度(修改之後len屬性的值)小於1MB,那麼則分配和len屬性同樣大小的未使用空間,這時SDS的len屬性和free屬性的值相同。如:如果修改之後SDS的len將變為10字節,那麼程序也會分配10字節的未使用空間,SDS的buf數組實際長度變為10 + 10 + 1 = 21(額外一個字節用於保存結束符\n)

SDS空間 > 1MB

如果對SDS修改之後,SDS的長度大於等於1MB,那麼程序會分配1MB的未使用空間。如:修改之後的len將變為10MB,那麼程序會分配1MB的未使用空間,SDS的bug數組長度為10MB + 1MB + 1byte

SDS空間 > 512MB

Game over~ 報錯!

惰性空間釋放

用於優化SDS的字符串收縮操作,當字符串收縮時,程序不會立即執行內存重分配來回收收縮后內存多出來的空間,而是使用free屬性記錄下來,以備將來使用。

通過空間預分配,Redis可以減少連續執行字符串增長操作所需的內存重分配次數,通過惰性空間釋放,SDS避免了縮短字符串時所需的內存重分配操作,併為將來由可能的增長操作提供了優化。

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

FB行銷專家,教你從零開始的技巧

聚甘新