系統架構設計師-軟件水平考試(高級)-論文-可靠性設計

系統架構設計師-軟件水平考試(高級)-論文-可靠性

前言

首先說一下為什麼這兩個月又沒消息了,因為這兩個月忙啊。

首先是接收上半年系統分析師的證書,並完成總結。其次是九月份PMP考試(4A通過,尚需努力),然後是十一月的軟考高項的考試。工作的事情就不談了,還好沒什麼私人事情需要處理。所以這兩個月沒什麼空寫博客,不過接下來應該會有一些時間來寫博客。

關於系統架構師這個分支,原本都打算完結了的。然後突然發現大家對系統架構師的論文比較感興趣,並且自從我上次透露了我有一個架構師/分析師的群后,陸陸續續不斷有人私信我加群。所以,就回過頭,再發一篇系統架構師的論文。並打算找時間,將自己系統分析師,PMP,項目管理師的知識整理出來。畢竟在過去的一年的時間,我連續通過系統架構師,系統分析師,PMP,並完成,參加了高項(雖然目前還不知道通過沒),我認為我的學習方法,知識體系等還是有一定作用的,希望對大家有所幫助。嘻嘻。

哦。差點忘了。由於我的架構師/分析師群是邀請制的,所以給你們群號,也是無法添加的。所以,如果有參加架構師/分析師的朋友,請私聊我。謝謝。

一,理論

(強調一下,圖片絕對清晰。如果看不清,請從新的頁面打開,或者下載下來)

論文

摘要:
本人於2015年11月參与浙江省某在線教育平台“外教一對一在線教育”項目,該項目為客戶提供了一對一歐美外教視頻教學,社交圈,公眾直播等功能提供全方位的軟件支撐,在該項目組中我擔任系統架構師崗位,主要負責整體架構設計與中間件選型。本文以該教育平台為例,主要討論了該系統有關可靠性方面的設計與應用,以及遇到的問題與解決方案。一方面通過負載均衡進行容錯技術中冗餘設計的實現,另一方面通過層次架構風格來明確系統結構體系,從而降低系統設計複雜度,提高系統可靠性。整個系統開發工作歷時18個月。目前,該系統已經穩定運行近一年半的時間。實踐證明,通過容錯設計,降低複雜度設計等,系統有效提高了可靠性,從而為公司業務提供持續穩定的服務支撐。

正文:
隨着國家對教育的越發重視,英語教育的市場份額逐步上升,其中用戶口語提升的需求越來越大。為此,一些公司開始提供與外國人聊天的平台。我所在公司決定從國際通訊領域進軍口語教育領域。為了這項戰略轉變,公司於2015年11月設計某在線教育平台系統(一下簡稱為“系統”)。該系統幫助人們與歐美外教進行面對面的口語交流和教學。其中隨意聊提供了一種類似QQ視頻通話,而正式課程還提供了H5互動課件與課後點評等,以提高教學質量。與此同時,還有公眾直播用於拉新,AI測試用於評定學院能力,降低成本。我參与了該項目的開發工作,擔任系統架構設計師職務,負責設計系統架構。本項目組全體成員共9人,我主要負責項目計劃制定,需求分析,整體架構設計與技術選型,以及部分底層設計。該項目的架構工作與次年2月完成,選擇了層次架構風格。整個項目耗時18個月,於2017年5月完成。
目前主流的可靠性設計技術有容錯設計,檢錯設計,降低複雜度設計等技術。容錯設計技術分為恢復塊設計,N版本程序設計和冗餘設計。其中恢復塊設計是選擇一組軟件操作作為容錯設計單元,將普通的程序塊編程恢復塊。N版本程序設計的核心是通過設計出多個模塊或不同版本,對於相同初始條件和相同輸入的操作結果,實現多數表決,防止其中某一軟件模塊/版本的故障提供錯誤的服務,以實現軟件容錯。冗餘設計是在一套完整的軟件系統之外,設計一種不同路徑,不同算法或不同實現方法的模塊或系統作為備份,在出現故障時可以使用冗餘的部分進行替換,從而維持軟件系統的正常運行。缺點是費用和資源的消耗會有所增加。檢錯技術是在軟件出現故障后能及時發現並報警。其缺點是不能自動解決故障。降低複雜度設計是因為軟件複雜性與軟件可靠性有着密切關係,是產生軟件缺陷的重要根源。在設計時考慮降低軟件的複雜性,是提高軟件可靠性的有效方法。

在了解系統需求后,我們決定聽從公司技術顧問的建議,容錯設計主要應用在冗餘設計方面,通過負載均衡,雙機容錯等機制完成冗餘設計。檢錯設計則是通過對Java異常處理機制的設計與封裝處理完成。至於降低複雜度方面,採用層次架構風格,使得系統的結構明確,立體,從而提高系統可靠性。接下來,我將從系統的冗餘設計,複雜度降低設計介紹可靠性在系統中的設計與應用,以及應用過程中遇到的問題與解決方案。

1.冗餘設計:

首先說冗餘設計,冗餘包含邏輯冗餘,數據冗餘,應用冗餘等。這裏以應用冗餘為例。為了提高系統的性能,可靠性,可拓展性等,我們採用了負載均衡技術。常見的負載均衡技術有F5硬件,LVS軟件,Nginx服務器配置等。出於便捷與成本的考慮,我們採用了Nginx服務器配置負載均衡技術。通過對Nginx服務器中upstream模塊的配置,就可以實現在多台服務器的反向代理家在負載均衡。採用負載均衡后,應用服務器集群存在Session問題無法統一的問題。解決方法有Session Sticky,Session Replication,Session數據集中存儲,Cookie Based四個方案。Session Sticky是通過確保同一個會話的請求都在同一個Web服務器上處理實現。Session Replication是增加Web服務器間會話數據的同步來保證不同Web服務器間的Session數據的一致。Cookie Based就是通過Cookie傳遞Session數據完成。經過考慮,我們採用了Session數據集中存儲。Session數據集中存儲通過令每台服務器從專門的session服務器獲取session數據來解決問題。優點是可靠性,可移植性與可拓展性的大幅提高。缺點是一方面讀寫Session數據引入了網絡操作,對數據讀取存在時延和不穩定性,但對於使用內網通信的系統並沒有太大影響。另一方面,如果Session服務器或集群出現問題,將會影響整個應用。我們通過雙機容錯機制解決該問題。除此之外,還有心跳線,看門狗等技術。限於篇幅,不再贅述。

2.降低複雜度設計:

再者就是降低複雜度設計,由於系統的複雜性和綜合性,我們決定採用層次架構風格,將系統架構分為接入層,應用層,服務層,數據層四個層次。這裏以應用層與服務層為例。應用層分為視圖層與業務邏輯層,視圖層負責App與網站的表現效果,業務邏輯層負責業務層的邏輯處理。為了解決系統日益複雜,應用日益臃腫問題,我們將系統按照應用橫向劃分,將系統劃分為課件管理系統,課程管理系統等十餘個子系統。如課件管理系統負責學員上課所用課件,有課件編輯,課件預覽,課件交互等多個功能模塊。功能模塊需調用服務層的服務支撐,如課件交互模塊需要調用stomp通信服務,實現學生與老師間課件的交互功能。另外,課件交互模塊通過對賬戶服務的調用,確立課件雙方的身份,從而明確雙方在課件交互過程中對課件交互部分的交互權限。該劃分使得系統體系變得清晰明了,極大降低系統複雜度,提高系統可靠性。應用層採用基於J2ee的MVC框架-Structs框架,主要通過Servlet和JSP技術實現。另外還有動靜分離,動態資源靜態化等,這裏不再贅述。

服務層提供通用服務。系統在應用層中按照應用橫向劃分,有效降低系統複雜度。但系統代碼仍然存在冗餘,比如用戶信息的調用在諸多應用子系統中都有相關模塊。另外應用的大小依舊十分巨大,複雜,而過小的應用劃分會增加數據庫連接數負擔,故我們提出服務化解決方案。服務化方案就是提取出各個應用的通用服務,如賬戶服務,Session服務等。出於技術成熟度與技術支持等考慮,我們最終採用了阿里的dubbo服務框架,建立服務層。開發過程中,產生了服務框架部署問題與實現服務框架的jar包和應用自身依賴的jar包衝突的問題。前者,我們通過Tomcat作為Web容器,而服務框架作為容器的一部分來解決。後者,我們通過Java的ClassLoader將服務框架自身用的類與應用的類進行隔離。除此之外,我們通過服務線程池隔離,分佈請求合併,服務調用端的流程控制來降低系統複雜度,提高系統可靠性。詳情限於篇幅,不再贅述。

最終項目成功上線,正常運行了近一年半,收到各方好評。尤其是H5課件的良好互動性,使得大量業界同行爭相模仿,改用H5製作課件。還有我們的服務化方案架構被作為許多傳統互聯網企業系統重構的經典方案。在系統的架構設計中,我們引入了層次架構的設計思想,有效地降低了維護成本,提高了系統的開放性,可擴展性,可重用性以及可移植性。當然還是存在一些問題的。如H5課件採用http協議,易被非法劫持,嵌入廣告,可以將協議修改為https來解決。還有我們採用的負載均衡算法是加權輪轉算法,過於簡單,常常出現資源分配不合理的現象,可以將算法改為加權最小連接數算法來解決。這些都是我在今後的系統設計和開發中需要注意與改進的地方,也是日後我應該努力的方向。

三,總結

這篇論文的項目,依舊是之前那片論文的項目-在線教育系統。但是其中很多技術,其實在原有項目中是沒有涉及的。

另外這篇論文與之前論文存在一個結構上的不同之處,那就是這次的核心論點只有兩個分論點。不過,第二個論點-降低複雜度設計,是通過兩個方面進行闡述的。這也算是論文中核心論點的一種回答方式。往往論文的核心論點,推薦使用三個分論點進行論述,而部分論文的核心論點就只能拆分為兩個分論點(或者,三個論點的拆分維度,自己不熟悉)。這時候就需要靈活的轉變自己的思想,將核心論點的兩個分論點氛圍主次論點回答,實際體現就是主論點兩個段落,次論點一個段落。

既然說到這裏,也說一下,如果核心論點可以拆分出多個分論點。如架構風格的層次架構完全可以拆分為接入層,應用層,服務層(基礎服務層,通用服務層,業務服務層),數據接入層,數據源等。那麼這種情況,我們完全可以從中挑選三點自己熟悉的部分,進行闡述。如果擔心這樣寫,文章顯得比較僵硬,就在相關位置寫上“此處,我們以XXX,XXX,XXX為重點,進行論述”這樣的話語即可。

附錄

早期未修改的論文:

摘要:
本人於2015年11月參与浙江省某在線教育平台“外教一對一在線教育”項目,該項目為客戶提供了一對一歐美外教視頻教學,社交圈,公眾直播等功能提供全方位的軟件支撐,在該項目組中我擔任系統架構師崗位,主要負責整體架構設計與中間件選型。本文以該教育平台為例,主要討論了該系統有關可靠性方面的設計與應用。一方面通過負載均衡與應用服務器集群實現容錯技術中冗餘設計的實現,另一方面通過建立了接入層,應用層,服務層,數據層四層層次的架構來降低明確系統結構,從而系統設計複雜度,提高系統可靠性。整個系統開發工作歷時18個月。目前,該系統已經穩定運行近一年半的時間。實踐證明,通過容錯設計,降低複雜度設計等,系統有效提高了可靠性,從而為公司業務提供持續穩定的服務支撐。

正文:
隨着國家對教育的越發重視,英語教育的市場份額逐步上升,其中用戶口語提升的需求越來越大。為此,一些公司開始提供與外國人聊天的平台。我所在公司決定從國際通訊領域進軍口語教育領域。為了這項戰略轉變,公司於2015年11月設計某在線教育平台系統(一下簡稱為“系統”)。該系統幫助人們與歐美外教進行面對面的口語交流和教學。其中隨意聊提供了一種類似QQ視頻通話,而正式課程還提供了H5互動課件與課後點評等,以提高教學質量。與此同時,還有公眾直播用於拉新,AI測試用於評定學院能力,降低成本。我參与了該項目的開發工作,擔任系統架構設計師職務,負責設計系統架構。本項目組全體成員共9人,我主要負責項目計劃制定,需求分析,整體架構設計與技術選型,以及部分底層設計。該項目的架構工作與次年2月完成,選擇了層次架構風格。整個項目耗時18個月,於2017年5月完成。

目前主流的可靠性設計技術有容錯設計,檢錯設計,降低複雜度設計等技術。容錯設計技術分為恢復塊設計,N版本程序設計和冗餘設計。其中恢復塊設計是選擇一組軟件操作作為容錯設計單元,將普通的程序塊編程恢復塊。N版本程序設計的核心是通過設計出多個模塊或不同版本,對於相同初始條件和相同輸入的操作結果,實現多數表決,防止其中某一軟件模塊/版本的故障提供錯誤的服務,以實現軟件容錯。冗餘設計是在一套完整的軟件系統之外,設計一種不同路徑,不同算法或不同實現方法的模塊或系統作為備份,在出現故障時可以使用冗餘的部分進行替換,從而維持軟件系統的正常運行。缺點是費用和資源的消耗會有所增加。檢錯技術是在軟件出現故障后能及時發現並報警。其缺點是不能自動解決故障。降低複雜度設計是因為軟件複雜性與軟件可靠性有着密切關係,是產生軟件缺陷的重要根源。在設計時考慮降低軟件的複雜性,是提高軟件可靠性的有效方法。

在了解系統需求后,我們決定聽從公司技術顧問的建議,在容錯設計,檢錯設計,降低複雜度設計三個主流方向分別作出相應處理和應用。容錯設計主要應用在冗餘設計方面,通過負載均衡,雙機容錯等機制完成冗餘設計。檢錯設計則是通過對Java異常處理機制的設計與封裝處理完成。至於降低複雜度,我們應用層次清晰的四層層次架構。通過將系統劃分為接入層,應用層,服務層,數據層,使得系統的結構明確,立體,從而降低系統複雜度。限於篇幅,接下來,我將從系統的冗餘設計,複雜度降低設計兩個方面介紹可靠性在系統中的設計與應用,以及應用過程中遇到的問題。

首先說冗餘設計,冗餘包含邏輯冗餘,數據冗餘,應用冗餘等。這裏以應用冗餘為例。一方面為了提高應用服務器性能,另一方面為了提高系統的可靠性,可拓展性等,我們採用了負載均衡技術。常見的負載均衡技術有F5硬件,LVS軟件,Nginx服務器配置等。出於便捷與成本的考慮,我們採用了Nginx服務器配置負載均衡技術。通過對Nginx服務器中upstream模塊的配置,就可以實現在多台服務器的反向代理家在負載均衡。為了提高負載均衡服務器可靠性,我們採用雙機熱備機制。但採用負載均衡后,應用服務器集群出現了Session問題無法統一的問題。解決方法有Session Sticky,Session Replication,Session數據集中存儲,Cookie Based四個方案。Session Sticky是通過確保同一個會話的請求都在同一個Web服務器上處理實現。Session Replication是增加Web服務器間會話數據的同步來保證不同Web服務器間的Session數據的一致。但一方面同步Session數據會造成網絡帶寬的開銷。另一方面,每台Web服務器都要保存所有Session數據,消耗大量內存。經過考慮,我們採用了第三種方案-Session數據集中存儲。Session數據集中存儲通過令每台服務器從專門的session服務器獲取session數據來解決問題。優點是可靠性,可移植性與可拓展性的大幅提高。缺點是一方面讀寫Session數據引入了網絡操作,對數據讀取存在時延和不穩定性,但對於使用內網通信的系統並沒有太大影響。另一方面,如果Session服務器或集群出現問題,將會影響整個應用。我們通過雙機容錯機制解決該問題。Cookie Based就是通過Cookie傳遞Session數據完成。實現簡單,但是存在如Cookie長度限制等問題。除此之外,還有心跳線,看門狗等諸多技術。限於篇幅,不再贅述。

再者就是降低複雜度設計,我們從架構風格選擇,技術選型等角度實現。由於系統的複雜性和綜合性,我們決定採用層次架構風格,將系統架構分為接入層,應用層,服務層,數據層四個層次。接入層負責多平台的接入,以及API網關,負載均衡等方面。API網關的使用使得對外資源與服務獲得統一,保持系統結構的明確,從而提高了系統可靠性。應用層分為視圖層與業務邏輯層,視圖層負責App與網站的表現效果,業務邏輯層負責業務層的邏輯處理。為了解決系統日益複雜,應用日益臃腫問題,我們將系統按照應用橫向劃分,將系統劃分為課件管理系統,課程管理系統等十餘個子系統。這樣的劃分使得系統體系變得清晰明了,極大降低系統複雜度,提高系統可靠性。應用層採用基於J2ee的MVC框架-Structs框架。服務層提供通用服務。系統在應用層中按照應用橫向劃分,有效降低系統複雜度。但系統代碼仍然存在冗餘,比如用戶信息的調用在諸多應用子系統中都有相關模塊。另外應用的大小依舊十分巨大,複雜,而過小的應用劃分會增加數據庫連接數負擔,故我們提出服務化解決方案。服務化方案就是提取出各個應用的通用服務,如賬戶服務,Session服務等。出於技術成熟度與技術支持等考慮,我們最終採用了阿里的dubbo服務框架,建立服務層。數據層涉及緩存,文件系統,數據庫,數據通知服務,搜索系統等模塊。由於用戶對數據訪問具有集中性,故我們基於Spring Cache與Redis實現緩存機制。數據訪問方面,Java已經有很多成熟技術,大致分為專用API方式,JDBC方式,給予ORM或類ORM接口方式三種。最終我們採用了成熟的ORM框架-Mybatis框架,再將框架包裝一層。這樣一方面提高系統開發效率,另一方面提高系統可移植性與可靠性。除此之外,還採用了solr作為數據層搜索引擎,數據訪問層物理部署採用Proxy方式。限於篇幅,不再贅述。

最終項目成功上線,正常運行了近一年半,收到各方好評。尤其是H5課件的良好互動性,使得大量業界同行爭相模仿,改用H5製作課件。還有我們的服務化方案架構被作為許多傳統互聯網企業系統重構的經典方案。在系統的架構設計中,我們引入了層次架構的設計思想,有效地降低了維護成本,提高了系統的開放性,可擴展性,可重用性以及可移植性。當然還是存在一些問題的。如H5課件採用http協議,易被非法劫持,嵌入廣告,可以將協議修改為https來解決。還有我們採用的負載均衡算法是加權輪轉算法,過於簡單,常常出現資源分配不合理的現象,可以將算法改為加權最小連接數算法來解決。這些都是我在今後的系統設計和開發中需要注意與改進的地方,也是日後我應該努力的方向。

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

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

撥亂反正-重構是門藝術活

前言

引用自: 《重構 改善既有代碼的設計》

重構是在不改變軟件可觀察行為的前提下改善其內部結構。當你面對一個最需要重構的遺留系統時,其規模之大、歷史之久、代碼質量之差,常會使得添加單元測試或者理解其邏輯都成為不可能的任務。此時你唯一能依靠的就是那些已經被證明是行為保持的重構手法: 用絕對安全的手法從焦油坑中整理出可測試的接口,給它添加測試,以此作為繼續重構的立足點。

因為我們部門內容平台的文章系統之前遺留了很多問題,急需解決這些具有”壞味道”的代碼。最後因為其他人手頭裡都有其他工作,最後這些任務就交給了我。以下是急需解決的問題。

  1. 內容平台新增/更新/取消/刪除文章,同步各集團下文章行為狀態,消息鏈路過長的問題。
  2. article分享錶停止規模新增,之前未做插入前的記錄判斷,通過新增的操作來進行記錄留存。
  3. 文章表拆除大字段到分表,如content、content_draft等字段。

鏈路過長概述

內容平台新增/更新/取消/刪除文章,同步各集團下文章行為狀態,消息鏈路過長的問題。

  • 問題導火索: 運營後台文章發布,發送消息到marketing-base

  • 慢鏈路,鏈路過長

    • mysql數據同步,單條執行n次

    • es索引數據同步,dubbo接口調用n次

圖1 鏈路圖

鏈路過長剖解及解決思路

具體問題,具體對待

//開啟同步開關的集團
        List<Integer> groupList = autoSyncStatusService.getAutoSyncGroupByManageType(MANAGE_TYPE_GROUP_ARTICLE); 

    for (Integer groupId : syncSubjectList) {
                SiteGroupInfoDTO siteGroupInfo = siteSPI.getGroupInfoById(groupId);
                Set<String> groupBrandSet = carOnSaleManage.getGroupBrandSet(siteGroupInfo);
                List<String> matchedBrandCodes = extractBrandCodesFromArticleLabel(article.getLabelInfos());
                if (CollectionUtils.isEmpty(matchedBrandCodes) || CollectionUtils.containsAny(groupBrandSet, matchedBrandCodes)) {
                    ArticleGroupMaterialBO groupMaterialBO =
                            ArticleBeanConverter.convertMaterial2GroupMaterial(article, groupId, groupList);
                    // 設置對應的集團主題id
                    ArticleGroupSubjectBO groupSubjectBO =
                            articleGroupSubjectService.getGroupSubjectBySoucheId(groupId, article.getSubjectId());
                    if (Objects.nonNull(groupSubjectBO.getId())) {
                        groupMaterialBO.setSubjectId(groupSubjectBO.getId());
                        groupMaterialBO.setMaterialId(myArticleId);
                        articleGroupMaterialService.addArticleGroupMaterial(groupMaterialBO);
                    }
            }
        } else {
                //查詢同步的文章數據是否存在
                List<ArticleGroupMaterialBO> list = articleGroupMaterialService.getListByMaterialId(myArticleId);
                for (ArticleGroupMaterialBO a : list) {
                    if (groupList.contains(a.getGroupId())) {
                        articleGroupMaterialService.changeRecommendStatus(a.getId(), a.getGroupId(), recommend, article.getLastOperatorName(), article.getLastOperatorName());
                    }
                }
        }
  • 第4行中我們可以看到這裡有一個for循環️,假設開啟同步開關的集體有1000家,則第18行中mysql插入操作就需要執行1000次。

  • 第24行這裏同樣有一個for循環體️,則26行內部的es數據同步則需要調用1000次。它的實現如下:

    @Override
        public boolean changeRecommendStatus(int id, int groupId, int recommended, String lastOperatorUserId, String lastOperatorName) {
            final boolean success = articleGroupMaterialDAO.changeRecommendStatus(
                    id, groupId, recommended, lastOperatorUserId, lastOperatorName) > 0;
            if (success) {
                //更新索引,更改推薦狀態
                articleSearchManage.updateArticleIndex(ArticleIndexUtil.getUpdateRecommendIndex(recommended, id, lastOperatorName));
            }
            return success;
        }

    解決思路

    Mybatis批量插入

    對於第一個循環️體中,我們需要將數據批量添加到數據庫,mybatis提供了將list集合循環添加到數據庫的方法。

    1. mapper層中創建 insertForeach(List < Fund > list) 方法,返回值是批量添加的數據條數
    public interface FundMapper {
      int insertForeach(List<Fund> list);
    }
    1. mybatis的xml文件中的insert語句如下
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.center.manager.mapper.FundMapper">
    
      <insert id="insertForeach" parameterType="java.util.List" useGeneratedKeys="false">
                  insert into fund
                  ( id,fund_name,fund_code,date_x,data_y,create_by,create_date,update_by,update_date,remarks,del_flag)
                  values
                  <foreach collection="list" item="item" index="index" separator=",">
                      (
                          #{item.id},
                          #{item.fundName},
                          #{item.fundCode},
                          #{item.dateX},
                          #{item.dataY},
                          #{item.createBy},
                          #{item.createDate},
                          #{item.updateBy},
                          #{item.updateDate},
                          #{item.remarks},
                          #{item.delFlag}
                      )
                   </foreach>     
        </insert>    
    </mapper>
    ES批量更新

    com.souche.elastic.search.api.IndexService

    方法:BulkUpdateResponse bulkUpdate(String index, Map<String, Object> event, String query, String origin)
    
    參數:
    
        index:要操作的索引
    
        event:更新的數據,可以只包含需要更新的字段,相當於mysql的update語句中的set語句中的字段
    
        query:query中的條件相當於mysql中的where,具體語法與下面的搜索接口中【querys:string 複雜的複合查詢 不同字段的OR 查詢】相同
    
        origin:操作源,一般寫調用方自己的應用名,用於區分不同調用方
    
    返回值:
    
        BulkUpdateResponse:
    
          {
    
            requestId:本次操作的唯一標示
    
            status:狀態,目前返回默認都是true
    
            updated:成功更新的條數
    
            failed:更新失敗的條數
    
            message:第一條更新失敗的原因
    
          }
    
    調用示例:
    1Map<String, Object> data = new HashMap<>();
    2        data.put("id", 20);
    3        data.put("title", "xue yin");
    4        data.put("content", "kuang dao");
    5        BulkUpdateResponse response = indexService.bulkUpdate("test_index", data, "address=bj AND contry=cn", "shenfl");

    這條更新將test_index索引中所有 address是bj並且contry是cn 的數據的 title更新成‘xue yin’ content更新成‘kuang dao’,注意:address和contry兩個字段在索引中需要加索引

Article表插入邏輯優化,停止規模新增概述

Article邏輯優化剖解及解決思路

具體問題及解決思路

當前article數據表數據量:

select count(*) as 總數 from article;

結果如下:

總數
369737
  @Override
    public String addSharedArticle(ArticleBO articleBO) {
        ArticleDO articleDO = new ArticleDO();
        BeanUtils.copyProperties(articleBO, articleDO);
        String shortUUID = UUIDUtil.getShortUUID();
        articleDO.setUid(shortUUID);
        if (articleDAO.addSharedArticle(articleDO) > 0) {
            return shortUUID;
        }
        return StringUtil.EMPTY_STRING;
    }

從上面這個業務邏輯實現類中,我們可以看到事實上我們想得到的是插入表數據的uid。但是之前的邏輯中,我們並沒有判斷該條數據是否已經存在,我們需要在上面代碼中判斷數據是否存在,已存在,查詢最後一天數據的uid返回給上層。不存在的話,執行插入操作。

文章表拆除大字段到分表

article_material表結構設計

article_material | CREATE TABLE `article_material` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `my_article_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '內容平台我的文章id',
  `status` tinyint(3) unsigned NOT NULL COMMENT '1-待發布、2-發布、3-取消發布',
  `subject_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '主題id',
  `platform_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '平台id',
  `source` varchar(32) NOT NULL DEFAULT '' COMMENT '版塊',
  `crawler_article_id` varchar(32) NOT NULL DEFAULT '0' COMMENT '爬蟲的文章id',
  `title` varchar(64) NOT NULL DEFAULT '' COMMENT '標題',
  `cover_img` varchar(128) NOT NULL COMMENT '封面圖',
  `summary` varchar(255) NOT NULL DEFAULT '' COMMENT '摘要',
  `labels` varchar(512) NOT NULL DEFAULT '' COMMENT '標籤',
  `label_infos` varchar(1024) NOT NULL DEFAULT '' COMMENT '標籤詳細信息',
  `content` text NOT NULL COMMENT '內容,用戶看到的',
  `content_imgs` text NOT NULL COMMENT '內容中圖片',
  `content_videos` varchar(255) NOT NULL DEFAULT '' COMMENT '內容中視頻',
  `content_draft` text NOT NULL COMMENT '草稿內容,編輯后保存到這裏,發布后內容會複製到content,此字段清空',
  `content_imgs_draft` text NOT NULL COMMENT '草稿內容的圖片,同上',
  `content_videos_draft` varchar(255) NOT NULL DEFAULT '' COMMENT '草稿內容的視頻',
  `recommended` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '0-不推薦、1-推薦',
  `author_user_id` varchar(64) NOT NULL DEFAULT '' COMMENT '作者userId',
  `author_name` varchar(16) NOT NULL COMMENT '作者名稱',
  `last_operator_user_id` varchar(64) NOT NULL DEFAULT '' COMMENT '最後操作人userId',
  `last_operator_name` varchar(16) NOT NULL COMMENT '最後操作人名字',
  `publish_date` datetime DEFAULT NULL COMMENT '發布時間',
  `publisher_user_id` varchar(64) NOT NULL DEFAULT '' COMMENT '發布者userId',
  `publisher_name` varchar(16) NOT NULL DEFAULT '' COMMENT '發布者名字',
  `pv` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '流量pv',
  `uv` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '流量uv',
  `share_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '分享次數',
  `share_people_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '分享人數',
  `date_create` datetime NOT NULL,
  `date_update` datetime NOT NULL,
  `date_delete` datetime DEFAULT NULL,
  `deleted` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '0 表示未刪除,刪除后是毫秒級時間戳',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_id` (`my_article_id`),
  KEY `idx_title_label_status` (`subject_id`,`platform_id`,`title`,`label_infos`(255),`source`)
) ENGINE=InnoDB AUTO_INCREMENT=861 DEFAULT CHARSET=utf8 COMMENT='文章素材庫,給集團提供文章素材'

上表中content, content_imgs,content_videos都是text類型等大字段,對於這種類型,我們需要把這種類型的表拆分成2張表 article_metedata和article_content 兩張表。

表拆分圖示

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

【其他文章推薦】

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

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

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

說服川普抗暖化 影后珍芳達曾想祭出美女戰術

摘錄自2019年12月18日中央社報導

珍芳達將在12月21日歡慶82歲生日,17日她在全國記者俱樂部(National Press Club)表示,她曾試圖在2016年美國總統川普當選後,安排包括女星潘蜜拉安德森在內的一群「美麗、性感、傑出」環保人士與川普會面,以說服他對付全球暖化問題。珍芳達(Jane Fonda)曾經跟川普女婿庫許納(Jared Kushner)和女兒伊凡卡(Ivanka Trump)討論她的想法,但並未得到回覆。最終這讓她搬到華盛頓住上幾個月,利用自己的名人力量動員群眾。

珍芳達曾分別以1971年「柳巷芳草」(Klute)與1978年「歸返家園」(Coming Home)兩部作品,摘下奧斯卡影后殊榮。

長年投身社會運動的珍芳達經常參與氣候變遷抗議活動,曾於今年10月在美國國會山莊外被警方逮捕。

 

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

【其他文章推薦】

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

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

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

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

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

燃煤電廠轉型燒生質能? 歐洲智庫警告:砍樹燒木屑 未必能減碳

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

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

風災後福島輻射污染擴散 學者提醒志工應佩戴口罩

文:宋瑞文(媽媽監督核電廠聯盟特約撰述)

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

【其他文章推薦】

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

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

Saft將供應61輛龐巴迪電車電池系統

世界級電池製造商Saft(帥福得)與法國龐巴迪(Bombardier)公司達成合作,將由Saft提供122組MX鎳鎘電池系統供龐巴迪61輛FLEXITY有軌電車運行於瑞士巴爾塞市。電池之生產與供貨將在2017年之前完成。

龐巴迪的FLEXITY有軌電車採低地板式設計,結合傳統的底盤結構,創造出更適合城市機動性要求的車體。2012年,龐巴迪與巴爾塞市交通部簽訂合約,巴爾塞市將出資1.84億歐元採購61輛FLEXITY有軌電車,用於取代既有的101輛有軌電車。

每輛FLEXITY有軌電車將搭載兩套Saft MSX 24V定制電池系統,為牽引系統提供緊急備用電源。當電纜供電中斷時,FLEXITY電車仍能繼續行駛到有電地段;而在沒有電纜的地區,Saft MSX也能驅動有軌電車短途行駛,增強機動性能。

Saft已在2013年為兩輛行駛於巴爾塞市的FLEXITY電車提供四套電池系統展開營運,而其餘59輛電車的電池將陸續開始交貨,預計在2017年完成。本次合約代表Saft在歐洲輕軌電車領域的業績成長,年成長率來到15%。

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

【其他文章推薦】

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

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

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

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

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

東風計畫擴建新能源基地 但仍按“舊”規劃發展產品

據報導,東風汽車將在旗下東風乘用車公司擴建一處生產基地,用於新能源汽車製造。

實際上,東風的新能源乘用車產能暫時並不存在缺口。目前,東風乘用車武漢生產基地的年產能為16萬輛,東風乘用車的銷量僅為10萬輛。此外,東風乘用車在常州也建有生產基地,可用於新能源汽車生產。

對此,一位接近東風公司的知情人士表示,東風規劃的新能源汽車生產基地,可能將被用於新產品開發和生產。

仍按“舊”規劃發展產品

實際上,東風很早以前就開始電動汽車研發。其內部資料顯示,東風在“九五”規劃期間就開始電動車技術研發,2010年設立了9個整車項目平臺,包括混合動力和純電動,涉及乘用車和商用車。

據瞭解,主營商用車業務的東風股份2015年新能源商用車銷量達到5191輛,同比增長25倍。此外在商用車領域,東風還推出了純電動客車、物流車等產品,在主流市場均有佈局。

然而,與上汽、北汽等已推出多款新能源乘用車相比,東風在這一領域的反應略顯滯後。

2015年被視作新能源汽車發展元年,銷量開始成倍增長,各品牌相繼加入這一領域,然而,東風依然在按照多年前制定的既定產品路線發展。

據瞭解,目前東風自主品牌的新能源乘用車僅有E30、E30L、A60EV三款車型。

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

特斯拉MODEL 3三月底發佈 預估售價約合人民幣22.77萬

據報導,特斯拉將於3月31日發佈全新MODEL 3車型,新車將於同期開始接受預訂,最快有望於2017年晚些時候交付客戶。新車的預估售價約為35000美元(約合人民幣22.77萬元)。

發佈當日,特斯拉將同期開始接受現場預訂,訂金為1000美元(約合人民幣6506元),次日(4月1日)起接受線上預訂,而新車實際交付將會於2017年晚些時候進行。根據消息,預售價為35000美元(約合人民幣22.77萬元)的MODEL 3作為入門車型,其電池續航里程或低於定位更高的MODEL S和MODEL X車型。即將發佈的MODEL 3車型為一款三廂轎車,但未來不排除有跨界版本出現的可能。

特斯拉CEO埃隆•馬斯克曾經表示,MODEL 3未來有望在中國投產,而價格預計只有MODEL S的一半。作為特斯拉的入門車型,MODEL 3將是一款肩負走量任務的產品,適合進行當地語系化生產。未來國產後,其價格可能會下降三分之一。

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

【其他文章推薦】

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

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

從代碼的視角深入淺出理解DevOps

對於DevOps的理解大家眾說紛紜,就連維基百科(Wikipedia)都沒有給出一個統一的定義。一般的解釋都是從字面上來理解,就是把開發(Development)和運維(Operations)整合到一起,來加速產品從啟動到上線的過程,並使之自動化。這個是對DevOps的廣義解釋,而且大多數人都是認可的。但這個解釋太寬泛了,幾乎包括了IT的所有內容,使之沒有太大意義。 而DevOps是近幾年才興起的(2014年才開始流行),它是對某種項目模式的描述,是有着其特定內涵的。任何項目都可以分成開發和運維兩個部分,而開發的一整套流程和工具在DevOps之前早就有了,並沒有改變。

DevOps真正改變的是運維。因此從運維的角度去理解DevOps,才能抓住它的本質。你可以把它理解為用開發的方式做運維(Operation as Development),這就是對它的狹義的理解。 開發的方式就是寫代碼,換句話說DevOps就是通過寫代碼來做運維。運維里一個非常流行的概念叫“Iac()” 基礎設施即代碼,也就是把運行環境的創建用代碼的形式來描述出來,通過運行代碼來創建環境。它是運維領域的一場革命,開創了現代運維技術,它也是DevOps的基石。但基礎設施創建只是運維的一部分,如果我們把這場革命繼續延伸到運維的各個領域,讓代碼覆蓋整個運維,那時就是代碼即運維(Operation as Code),這才是DevOps的精髓。

那麼從一個應用程序項目的角度看,什麼是DevOps呢?它就是把應用程序的代碼和運維的代碼都放到一個源程序庫中,並對它進行版本管理,這樣你就擁有了關於這個項目的所有信息,隨時可以部署這個程序(包括程序本身和它的運行環境),而且可以保證每次創建出來的程序的運行結果是一樣的(因為它的運行環境也是一樣的)。

運維即代碼(Operation as Code):

下面我們就把運維所做的事情一件一件拆分開,看看他們是怎麼用代碼來實現的。

運維的工作通常包括下面幾個方面:

  • 基礎設施:即程序運行環境的創建和維護。
  • 持續部署:部署應用程序,並使整個過程自動化。
  • 服務的健壯性:是指當服務的的運行環境出現了問題,例如網絡故障或服務過載或某些微服務宕機的情況下,程序仍能夠提供部分或大部分服務。
  • 運行監測:它既包含對程序的監測也包含對運行環境的監測。

基礎設施即代碼 (Infrastructure as code)

我們通過一個Go(別的語言也大同小異)微服務程序做例子來展示如何用代碼來創建基礎設施。程序本身的功能非常簡單,只是用SQL語句訪問數據庫中的數據,並寫入日誌。你可以簡單地把它分成兩層,後端數據訪問層和數據庫層。程序的部署環境是基於k8s的容器環境。在k8s中它被分成兩個服務。一個是後端程序服務,另一個是數據庫(用MySQL)服務。後端程序要調用數據庫服務,然後會把一些數據寫入日誌。

在這種新的模式下,運行環境的代碼和應用程序的代碼是存在一個源碼庫中的,這樣當你下載了源碼庫之後,你不但擁有了程序的所有源碼,而且也擁有了運行環境的源碼。這樣當要創建新的運行環境時,只要運行一遍代碼就能創建出整套的運行環境,而且每次創建出來的環境都是一致的。

上面就是這個Go程序的目錄結構,它裏面有一個目錄“script”是專門存放與運行環境相關的文件的,裏面的“kuburnetes”子目錄就是整個運行環境的代碼。除了“script”之外的其它目錄存有應用程序的代碼。這樣,與這個應用程序有關的信息都以代碼的方式保存在了一個源代碼庫。有了它之後,你可以隨時部署出一個相同的程序的運行環境,而且保證是一模一樣的。

“kubernetes”目錄下有兩個子目錄“backend”和“database”分別存放後端程序和數據庫的配置文件。它們內部的結構是類似的,都有三個“yaml”文件:

  • backend-deployment.yaml:部署配置文件,
  • backend-service.yaml:服務配置文件
  • backend-volume.yaml:持久卷配置文件.

另外還有一個“.sh”文件是它的運行命令,當你運行這個shell文件時,它就調用上面三個k8s配置文件來創建運行環境。

kubernetes目錄的最外層有兩個“yaml”文件“k8sdemo-config.yaml”和”k8sdemo-secret.yaml”,它們是用來創建k8s運行環境參數的,因為它們是被不同服務共享的,因此放在最外層。另外還有一個”k8sdemo.sh”文件是k8s命令文件,用來創建k8s對象。

這種源程序結構的一個好處就是使應用程序和它的運行環境能夠更好地集成。舉個例子,當你要修改服務的端口時,以前,你需要在運行環境和源碼里分別修改,但它是分別由開發和運維完成的,這很容易造成修改的不同步。當你把它們放在同一個源碼庫中,只需要修改一個地方,這樣就保證了應用程序和運行環境的一致性。

下面就是後端服務的k8s配置代碼:

apiVersion: v1
kind: Service
metadata:
  name: k8sdemo-backend-service
  labels:
    app: k8sdemo-backend
spec:
  type: NodePort
  selector:
    app: k8sdemo-backend
  ports:
    - protocol : TCP
      nodePort: 32080
      port: 80
      targetPort: 8080

由於篇幅的關係,這裏就不詳細解釋程序了,有興趣的請參見.

基礎設施可以分成兩個層面,一個就是上面講到的k8s層面,也就是容器層面,這個是跟應用程序緊密相關的。還有一個層面就是容器下面的支持層,也就是虛機層面,當然還包括網絡,負載均衡等設備或軟件。當你在阿里雲或華為雲上創建k8s之前,先要把這些構建好才行。它的部署也可以用代碼來完成。Terraform就是一款非常流行的用來完成創建的工具,它是被ThoughtWorks推薦的(詳見 ),它支持用代碼來創建虛機。

代碼如下:

resource "aws_instance" "example" {
  count         = 10
  ami           = "ami-v1"
  instance_type = "t2.micro"
}

但在這一層面,基礎設施的工作與應用程序的關聯並沒有那麼緊密,因此這部分的代碼沒有放在應用程序里,你可以單獨創建一個基礎設施的源碼項目,用來存放這部分代碼。

持續部署(Continuous Deployment)

部署應用程序是運維的一項重要工作。隨着商業競爭的加劇,要求更快的程序更新,從原來的的幾周部署一次,到後來的一天部署十幾次甚至幾十次。這樣手工部署就完全不能滿足需要,於是就要把整個流程自動化,這就是持續部署。

管線(pipeline)是一個很重要的概念,它用來描述持續部署的整個步驟和流程。Jenkin是一款非常流行的持續集成和部署工具,它提出了“管線即代碼”(“Pipeline as code”,詳見)。就是把持續部署的管線也作為程序源碼的一部分,和應用程序一起管理起來,讓它有着和應用程序一樣的版本和複審流程。

下面我們就通過一個具體例子來說明他是怎樣實現的。這個例子用的是和上面一樣的程序。先來看一下程序的目錄結構。

與持續部署相關的文件都在“script”目錄下,他被分成兩部分,一個是“cd”子目錄,它存有Jenkins的管線,另一個是“Kubernetes”下的“jenkins”子目錄,它存有Jenkinsde的k8s配置文件。你如果仔細看一下的話會發現它裏面的文件和前面講到的後端程序和數據庫的k8s配置文件很相似,有了它,你就可以在k8s里創建出Jenkins的運行環境。

這裏我把Jenkins的k8s配置文件也放在應用程序里了,但實際上它是應該放在前面提到的基礎設施項目源碼里。因為Jenkins是被應用程序共享的,而不是屬於單獨的一個應用。這裏為了說明方便放在一起了,真正用的時候要把它抽取出來。

下面就是管線的代碼:

def POD_LABEL = "k8sdemopod-${UUID.randomUUID().toString()}"
podTemplate(label: POD_LABEL, cloud: 'kubernetes', containers: [
    containerTemplate(name: 'modified-jenkins', image: 'jfeng45/modified-jenkins:1.0', ttyEnabled: true, command: 'cat')
  ],
  volumes: [
     hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
  ]) {

    node(POD_LABEL) {
       def kubBackendDirectory = "/script/kubernetes/backend"
       stage('Checkout') {
            container('modified-jenkins') {
                sh 'echo get source from github'
                git 'https://github.com/jfeng45/k8sdemo'
            }
          }
       stage('Build image') {
            def imageName = "jfeng45/jenkins-k8sdemo:${env.BUILD_NUMBER}"
            def dockerDirectory = "${kubBackendDirectory}/docker/Dockerfile-k8sdemo-backend"
             container('modified-jenkins') {
               withCredentials([[$class: 'UsernamePasswordMultiBinding',
                 credentialsId: 'dockerhub',
                 usernameVariable: 'DOCKER_HUB_USER',
                 passwordVariable: 'DOCKER_HUB_PASSWORD']]) {
                 sh """
                   docker login -u ${DOCKER_HUB_USER} -p ${DOCKER_HUB_PASSWORD}
                   docker build -f ${WORKSPACE}${dockerDirectory} -t ${imageName} .
                   docker push ${imageName}
                   """
               }
             }
           }
       stage('Deploy') {
           container('modified-jenkins') {
               sh "kubectl apply -f ${WORKSPACE}${kubBackendDirectory}/backend-deployment.yaml"
               sh "kubectl apply -f ${WORKSPACE}${kubBackendDirectory}/backend-service.yaml"
             }
       }
    }
}

由於篇幅的關係,這裏不詳細解釋。如果有興趣並想了解如何運行Jenkins來完成持續部署,請參閱 .

這裏我用的是Jenkins軟件,它另外還有一個子項目叫Jenkins-x,是專門為k8s環境量身打造的,它的主要功能是能夠幫助你自動生成管線代碼(你需要回答他的一些問題)。如果你不想自己寫代碼,那麼你可以試一下它,詳情請見。

服務的韌性(Service Resilience as Code)

又叫服務的健壯性,這部分不像前面兩個部分有着公認的名字,英文叫“Service Resilience”,翻譯成中文就五花八門了,我覺得叫服務的韌性比較合適。

“Service Resilience”是指當服務的的運行環境出現了問題,例如網絡故障或服務過載或某些微服務宕機的情況下,程序仍能夠提供部分或大部分服務,這時我們就說服務的韌性很強。它是衡量服務質量的一個重要指標。

這部分的功能包括下面幾個部分:

  • 服務超時 (Timeout)
  • 服務重試 (Retry)
  • 服務限流(Rate Limiting)
  • 熔斷器 (Circuit Breaker)
  • 故障注入(Fault Injection)
  • 艙壁隔離技術(Bulkhead)

這部分與前面兩個部分略有不同,前面兩個部分都是典型的運維任務,而這部分以前是應用程序的一部分,只是這些年才慢慢開始轉移到運維的。

最開始的時候,這些功能都是和程序的業務邏輯混在一起,對業務邏輯的侵入很大,後來,大家開始把這部分邏輯抽取出來,劃分成單獨的一部分。下面通過一個具體的例子(Go微服務程序)來講解:

上圖是程序的目錄結構,它分為客戶端(client)和服務端(server),它們的內部結構是類似的。“middleware”包是實現服務韌性功能的包。 “service”包是業務邏輯包,在服務端就是微服務的實現函數,在客戶端就是調用服務端的函數。在客戶端(client)下的“middleware”包中包含四個文件並實現了三個功能:服務超時,服務重試和熔斷器。“clientMiddleware.go”是總入口。在服務端(server)下的“middleware”包中包含了兩個文件並實現了一個功能,服務限流。“serverMiddleware.go”是總入口。

注意,這裏的服務韌性的功能是完全從業務邏輯中抽出來了,對業務邏輯沒有任何侵入,它是在一個單獨的包(middleware)里實現的。這裏用的是修飾模式。有關程序實現的詳情,請參閱。

上面講的是用程序來實現這些功能,但從本質上來講這些功能不應該屬於應用程序,而是應該由基礎設施來完成。現在公認的看法是,服務網格(Service Mesh)是完成這些功能的最佳方案。使用服務網格的方式和k8s類似,也是創建配置文件,然後通過運行配置文件來建立服務網格的運行環境。我們這裏用Istio來舉例說明,Istio是一款非常流行的服務網格軟件。

上圖就是下載的Istio軟件的目錄,“bookinfo”是它的一個示例程序,在這個例子里,它展示了多種使用Istio的方式,其中就有如何實現服務韌性的方法。詳情請參見.

運行監測 (Monitoring or Observability)

運行監測是運維的一項重要內容,它通常包含如下幾個方面的內容:

  • 日誌(logging): 記錄的是程序運行過程中的信息
  • 跟蹤(tracing): 記錄的是與一條請求相關的信息,特別是請求的與時間有關的信息。
  • 指標(metrics): 與上面的離散的信息不同,這裏記錄的是可累加的信息,一般是按照時間軸進行累加。

我們經常稱之為觀測的三個支柱(Three Pillars of Observabilty),有一篇很不錯的講解它們之間關係的文章,詳情請見””。

這部分的內容可能會有些爭議。因為前幾個部分都是清清楚楚的運維工作,即使服務韌性, 雖然以前是開發的工作,但現在也已經一直公認是運維的事,而且它們的代碼都能很乾凈的摘出來。但運行監測不一樣,雖然主要工作還是由運維來完成,但它的代碼與業務邏輯代碼混在一起,很難摘得清楚。

日誌:

這部分的代碼都是在應用程序里,但日誌的採集,匯總,分析和展示都是由運維來完成。它的代表就是著名的ELK系列。採用DevOps之後,這裏面的變化不大,頂多就是採集代理(Agent)更好地和服務網格或k8s進行集成,使之變得更為容易。

分佈式跟蹤:

這部分有點像服務韌性,開始的時候是由程序完成,慢慢地把它變成單獨的部分與應用程序隔離開,最終大部分的工作交由服務網格來完成。但它又與服務韌性不太相同。服務韌性可以和應用程序做一個非常乾淨的切割,而分佈式跟蹤取決於跟蹤的顆粒度。如果僅是服務之間的跟蹤,就一點問題都沒有,完全可以由服務網格來完成。但如果是服務內部的跟蹤,服務網格就無能為力了,還是要由程序代碼來完成,就像日誌一樣。但我覺得服務之間的跟蹤是投入產出比最高的,大多數情況下有它就足夠了,不必需要服務內部的跟蹤。

詳細情況請參見

Metrics:

這部分觀測的是累加信息。大多數情況下,只要安裝好工具,就能採集數據進行分析。最流行的工具是Prometheus. 你不需要寫代碼來獲取數據,不過你如果想要快速地找到需要的信息,k8s的配置還是要和Prometheus的設置相匹配,因此你需要做一些詳細的設計。詳細情況請參見。

當然你如果有一些更細緻的監測需求,Prometheus不能直接滿足。這時需要在應用程序里插入一些Prometheus的監測代碼來滿足你的需要。

其他工作

是不是還有其他運維工作被漏掉了?

持續集成(Continious Integration)

很多人都把持續集成(CI)算作DevOps的重要組成部分,那是因為他用的是廣義的定義。按照狹義的理解,DevOps只包括運維的內容。持續集成(CI)與持續部署(CD)有着明顯的不同,持續集成是開發的工作,而持續部署是運維的工作,下圖展示了它們的差異。

如圖所示,整個流程是這樣的:
程序員從源碼庫(Source Control)中下載源代碼,編寫程序,完成后提交代碼到源碼庫,持續集成(Continuous Integration)工具從源碼庫中下載源代碼,編譯源代碼,然後提交到運行庫(Repository),然後持續交付(Continuous Delivery)工具從運行庫(Repository)中下載代碼,生成發布版本,併發布到不同的運行環境(例如DEV,QA,UAT, PROD)。

圖中,左邊的部分是持續集成,它主要跟開發和程序員有關;右邊的部分是持續部署,它主要跟測試和運維有關。持續交付(Continuous Delivery)又叫持續部署(Continuous Deployment),它們如果細分的話還是有一點區別的,但我們這裏不分得那麼細,統稱為持續部署

我並沒有把持續集成放到DevOps裏面,因為本文用的是狹義的解釋,也就是只包含運維的部分。

結論

本文從代碼的視角詮釋了對DevOps的理解,DevOps的精髓就是用寫代碼的方式來做運維,並對運維的各個部分給出了具體的實例,希望能對想採用DevOps的朋友有所幫助。DevOps對開發和運維的改變都是巨大的,尤其是對運維。在不久的將來,就沒有開發和運維之分了,只有一個工作,就是寫代碼,當然也許會細分成開發碼農和運維碼農。運維的工作都是通過寫代碼來完成。應用程序里不但包括業務邏輯的代碼,也包括運維的代碼,它們會被同時存儲在一個源碼庫中。

源碼庫

完整源碼的github鏈接:

索引:

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

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

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

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

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

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

Java基礎系列6:深入理解Java異常體系

該系列博文會告訴你如何從入門到進階,一步步地學習Java基礎知識,並上手進行實戰,接着了解每個Java知識點背後的實現原理,更完整地了解整個Java技術體系,形成自己的知識框架。

 

前言:

Java的基本理念是“結構不佳的代碼不能運行”。

“異常”這個詞有“我對此感到意外”的意思。問題出現了,你也許不清楚該如何處理,但你的確知道不應該置之不理;你要停下來,看看是不是有別人或在別的地方,能夠處理這個問題。只是在當前的環境中還沒有足夠的信息來解決這個問題,所以就把這個問題提交到一個更高級別的環境中,在這裏將作出正確的決定。

使用異常所帶來的另一個相當明顯的好處是,它往往能夠降低錯誤處理代碼的複雜度。如果不使用異常,那麼就必須檢查特定的錯誤,並在程序中的許多地方去處理它。而如果使用異常,那就不必在方法調用處進行檢查,因為異常機制將保證能夠捕獲這個錯誤。並且,只需在一個地方處理錯誤,即所謂的異常處理程序中。這種方式不僅節省代碼,而且把“描述在正常執行過程中做什麼事”的代碼和“出了問題怎麼辦”的代碼相分離。總之,與以前的錯誤處理方法相比,異常機制使代碼的閱讀、編寫和調試工作更加井井有條。

 

異常概述:

現在我們需要編寫一個五子棋程序,當用戶輸入下期坐標時,程序要判斷用戶輸入是否合法,如果保證程序有較好的容錯性,將會有如下的代碼(偽代碼):

if(用戶輸入包含除逗號之外的其他非数字字符)
{
	alert 坐標只能是數值
	goto retry
}
else if(用戶輸入不包含逗號) {
	alert 應使用逗號分隔兩個坐標值
	goto retry
}

else if(用戶輸入坐標值超出了有效範圍) {
	alert 用戶輸入坐標應位於棋盤坐標之內
	goto retry
}

else if(用戶輸入的坐標已有棋子)
{
	alert 只能在沒有棋子的地方下棋
	goto retry
}
else {
	.....
}

  

上面代碼還未涉及任何有效處理,只是考慮了4種可能的錯誤,代碼就已經急劇增加了。但實際上,上面考慮的4種情形還遠未考慮到所有的可能情形(事實上,世界上的意外是不可窮舉的),程序可能發生的異常情況總是大於程序員所能考慮的意外情況。

對於上面的錯誤處理機制,主要有以下兩個缺點:

  • 無法窮舉所有的異常情況。因為人類知識的限制,異常情況總比可以考慮到的情況多,總有“漏網之魚”的異常情況,所以程序總是不夠健壯。
  • 錯誤處理代碼和業務實現代碼混雜。這種錯誤處理和業務實現混雜的代碼嚴重影響程序的可讀性,會增加程序維護的難度。

我們希望有這樣一種處理機制:

if(用戶輸入的數據不合法){
    .....
}else{
   處理邏輯
   .....  
}

  

上面偽碼提供了一個非常強大的“if塊”——程序不管輸入錯誤的原因是什麼,只要用戶輸入不滿足要求,程序就一次處理所有的錯誤。這種處理方法的好處是,使得錯誤處理代碼變得更有條理,只需在一個地方處理錯誤。

這就需要用到java異常了。

 

異常是程序中的一些錯誤,但並不是所有的錯誤都是異常,並且錯誤有時候是可以避免的。

比如說,你的代碼少了一個分號,那麼運行出來結果是提示是錯誤java.lang.Error;如果你用System.out.println(11/0),那麼你是因為你用0做了除數,會拋出java.lang.ArithmeticException的異常。

異常發生的原因有很多,通常包含以下幾大類:

  • 用戶輸入了非法數據。
  • 要打開的文件不存在。
  • 網絡通信時連接中斷,或者JVM內存溢出。

 

Java中的異常有以下三種類型:

檢查異常:最具代表的檢查性異常是用戶錯誤或問題引起的異常,這是程序員無法預見的。例如要打開一個不存在文件時,一個異常就發生了,這些異常在編譯時不能被簡單地忽略。

運行異常:運行時異常是可能被程序員避免的異常。與檢查性異常相反,運行時異常可以在編譯時被忽略。

錯誤:錯誤不是異常,而是脫離程序員控制的問題。錯誤在代碼中通常被忽略。例如,當棧溢出時,一個錯誤就發生了,它們在編譯也檢查不到的。

 

異常的體繫結構:

我們先來統觀以下Java的異常體繫結構圖:

 

 

Java的異常被分為兩大類:Checked異常和Runtime異常(運行時異常)。所有的RuntimeException類及其子類的實例被稱為Runtime異常;不是RuntimeException類及其子類的異常實例則被稱為Checked異常。

只有Java語言提供了Checked異常,其他語言都沒有提供Checked異常。Java認為Checked異常都是可以被處理(修復)的異常,所以Java程序必須顯式處理Checked異常。如果程序沒有處理Checked異常,該程序在編譯時就會發生錯誤,無法通過編譯。

 

Throwable

Java異常的頂級類,所有的類都繼承自Throwable

Error:

Error錯誤,一般是指與虛擬機相關的問題,如系統崩潰、虛擬機錯誤、動態鏈接失敗等,這種錯誤無法恢復或不可能捕獲,將導致應用程序中斷。通常應用程序無法處理這些錯誤,因此應用程序不應該試圖使用catch 塊來捕獲Error對象。
在定義該方法時,也無須在其throws子句中聲明該方法可能拋出Error及其任何子類。

 

Exception:

Exception中異常主要分為兩大類,運行時異常和檢查異常。常見的運行時異常有ArrayIndexOutOfBoundsException(數組下標越界)、NullPointerException(空指針異常)、ArithmeticException(算術異常)、ClassNotFoundException(類型找不到),這些異常時非檢查異常,程序可以選擇處理,也可以不處理,編譯器編譯時期並不會報錯。這些異常一般是由於程序邏輯錯誤引起的,所以建議程序員還是處理一下。除運行時異常外的所有異常我們都稱為非運行時異常,也是必須處理的異常,如果不出來,程序編譯會報錯。

 

Error和Exception的區別:

ErrorException的區別:Error通常是災難性的致命的錯誤,是程序無法控制和處理的,當出現這些異常時,Java虛擬機(JVM)一般會選擇終止線程;Exception通常情況下是可以被程序處理的,並且在程序中應該盡可能的去處理這些異常。

 

檢查異常:必須處理的異常

除了RuntimeException及其子類以外,其他的Exception類及其子類都屬於檢查異常,當程序中可能出現這類異常,要麼使用try-catch語句進行捕獲,要麼用throws子句拋出,否則編譯無法通過。

 

非檢查異常:可以不處理

包括RuntimeException及其子類和Error

不受檢查異常為編譯器不要求強制處理的異常,檢查異常則是編譯器要求必須處置的異常。

 

異常處理機制:

Java的異常處理機制可以讓程序具有極好的容錯性,讓程序更加健壯。當程序運行出現意外情形時,系統會自動生成一個Exception對象來通知程序,從而實現將“業務功能實現代碼”和“錯誤處理代碼”分離,提供更好的可讀性。

java異常關鍵字:

  • try – 用於監聽。try后緊跟一個花括號括起來的代碼塊(花括號不可省略),簡稱try塊,它裏面放置可能引發異常的代碼,當try語句塊內發生異常時,異常就被拋出。【監控區域】
  • catch – 用於捕獲異常。catch后對應異常類型和一個代碼塊,用於處理try塊發生對應類型的異常。【異常處理程序】
  • finally – 用於清理資源,finally語句塊總是會被執行。 多個catch塊后還可以跟一個finally塊,finally塊用於回收在try塊里打開的物理資源(如數據庫連接、網絡連接和磁盤文件)。只有finally塊執行完成之後,才會回來執行try或者catch塊中的return或者throw語句,如果finally中使用了return或者throw等終止方法的語句,則就不會跳回執行,直接停止。【使用finally進行清理】
  • throw – 用於拋出一個實際的異常。throw可以單獨作為語句使用,拋出一個具體的異常對象。【拋出異常】
  • throws –用在方法簽名中,用於聲明該方法可能拋出的異常。【異常說明】

 

1、使用try…catch捕獲異常:

語法格式:

try{
   //業務實現代碼
   ...
}catch(Exception e){
  //異常處理代碼
  ...
}

  

如果執行try塊里的業務邏輯代碼時出現異常,系統自動生成一個異常對象,該異常對象被提交給Java運行時環境,這個過程被稱為拋出(throw)異常。

當Java運行時環境收到異常對象時,會尋找能處理該異常對象的catch塊,如果找到合適的catch塊,則把該異常對象交給該catch塊處理,這個過程被稱為捕獲(catch)異常;如果Java運行時環境找不到捕獲異常的catch塊,則運行時環境終止Java程序也將退出。

下面看幾個簡單的異常捕獲的例子:

例1:

public class DivTest {
	
	public static void main(String[] args) {
		try {
			int a=Integer.parseInt(args[0]);
			int b=Integer.parseInt(args[1]);
			int c=a/b;
			System.out.println("您輸入的兩個數相除的結果是"+c);
		}catch(IndexOutOfBoundsException e) {
			System.out.println("數組越界,運行時參數不夠");
		}catch(NumberFormatException e) {
			System.out.println("数字格式異常");
		}catch(ArithmeticException e) {
			System.out.println("算術異常");
		}catch(Exception e) {
			System.out.println("未知異常");
		}
	}

}

  

  • 如果運行該程序時輸入的參數不夠,將會發生數組越界異常,Java運行時將調用IndexOutOfBoundsException對應的catch塊處理該異常。
  • 如果運行該程序時輸入的參數不是数字,而是字母,將發生数字格式異常,Java運行時將調用NumberFormatException 對應的catch塊處理該異常。
  • 如果運行該程序時輸入的第二個參數是0,將發生除0異常,Java運行時將調用ArithmeticException對應的catch塊處理該異常。
  • 如果程序運行時出現其他異常,該異常對象總是Exception類或其子類的實例,Java運行時將調用Exception對應的catch塊處理該異常。

上面程序中的三種異常是我們在編程中經常遇見的,讀者應該掌握這些異常。

例2:

import java.util.Date;


public class Test {
	
	public static void main(String[] args) {
		Date d=null;
		try {
			System.out.println(d.after(new Date()));
		}catch(NullPointerException e) {
			System.out.println("空指針異常");
		}catch(Exception e) {
			System.out.println("未知異常");
		}
	}

}

  

上面程序針對NullPointerException異常提供了專門的異常處理塊。上面程序調用一個null對象的after0方法,這將引發NullPointerException異常(當試圖調用一個null對象的實例方法或實例變量時,就會引發NullPointerException異常),Java 運行時將會調用NullPointerException對應的catch塊來處理該異常;如果程序遇到其他異常,Java運行時將會調用最後的catch塊來處理異常。

catch塊處理異常遵循着:先小后大,即先子類後父類。正如在前面程序所看到的,程序總是把對應Exception類的catch塊放在最後,這是為什麼呢?讀者可能明白原因:如果把Exception類對應的catch塊排在其他catch塊的前面,Java運行時將直接進入該catch塊(因為所有的異常對象都是Exception或其子類的實例),而排在它後面的catch塊將永遠也不會獲得執行的機會。

 

多異常捕獲:

在Java7之前,每個catch塊只能捕獲一個異常,Java7之後,每個catch塊可以捕獲多種類型的異常。

上面的例1可以改成如下代碼實現:

public class Test {
	
	public static void main(String[] args) {
		try {
			int a=Integer.parseInt(args[0]);
			int b=Integer.parseInt(args[1]);
			int c=a/b;
			System.out.println("您輸入的兩個數相除的結果是"+c);
		}catch(IndexOutOfBoundsException|NumberFormatException|ArithmeticException e) {
			System.out.println("數組越界,数字格式異常,算術異常");
		}catch(Exception e) {
			System.out.println("未知異常");
		}
	}

}

  

2、使用throws聲明拋出異常:

使用throws聲明拋出異常的思路是,當前方法不知道如何處理這種類型的異常,該異常應該由上一級調用者處理;如果main方法也不知道如何處理這種類型的異常,也可以使用throws聲明拋出異常,該異常將交給JVM處理。JVM對異常的處理方法是,打印異常的跟蹤棧信息,並中止程序運行,這就是前面程序在遇到異常后自動結束的原因。

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class Test {
	
	public static void main(String[] args) throws FileNotFoundException {
		FileInputStream fis=new FileInputStream("a.txt");
	}

}

  

上面程序聲明不處理IOException異常,將該異常交給JVM處理,所以程序一旦遇到該異常,JVM就會打印該異常的跟蹤棧信息,並結束程序。運行上面程序,會看到如下圖所示的運行結果。

 

 

3、使用throw拋出異常:

public class Test {
	
	public static void main(String[] args) throws Exception {
		try {
			int a=Integer.parseInt(args[0]);
			int b=Integer.parseInt(args[1]);
			int c=a/b;
			if(b==0) {
				throw new Exception("除數不能為0");
			}
			System.out.println("您輸入的兩個數相除的結果是"+c);
		}catch(Exception e) {
			System.out.println("未知異常");
		}
	}

}

  

上面程序中粗體字代碼使用throw語句來自行拋出異常。當Java運行時接收到開發者自行拋出的異常時,同樣會中止當前的執行流,跳到該異常對應的catch塊,由該catch塊來處理該異常。也就是說,不管是系統自動拋出的異常,還是程序員手動拋出的異常,Java運行時環境對異常的處理沒有任何差別。

 

4、訪問異常信息:

如果程序需要在catch塊中訪問異常對象的相關信息,則可以通過訪問catch塊的后異常形參來獲得。當Java運行時決定調用某個catch塊來處理該異常對象時,會將異常對象賦給catch塊后的異常參數,程序即可通過該參數來獲得異常的相關信息。

所有的異常對象都包括如下幾個常用的方法:

getMessage():返回該異常信息的跟蹤棧信息輸出到標準錯誤輸出

printStackTrace():將該異常的跟蹤棧信息輸出到標準錯誤輸出。

printStackTrace(PrintStream s):將該異常的跟蹤棧信息輸出到指定輸出流。

getStackTrace():返回該異常的跟蹤棧信息。

 

5、使用finally回收資源:

有些時候,程序在try塊里打開了一些物理資源(例如數據庫連接、網絡連接和磁盤文件),這些物理資源都必須显示回收。

在哪裡回收這些物理資源呢?在try塊里回收?還是在catch塊中進行回收?假設程序在try塊里進行資源回收,根據圖10.1所示的異常捕獲流程—一如果try塊的某條語句引起了異常,該語句后的其他語句通常不會獲得執行的機會,這將導致位於該語句之後的資源回收語句得不到執行。如果在catch塊里進行資源回收,但catch塊完全有可能得不到執行,這將導致不能及時回收這些物理資源。

為了保證一定能回收try塊中打開的物理資源,異常處理機制提供了finally塊。不管try塊中的代碼是否出現異常,也不管哪一個catch塊被執行,甚至在try塊或catch塊中執行了return語句,finally塊總會被執行。完整的Java異常處理語法結構如下:

try{
  //業務實現代碼
  ...
}catch(SubException e){
  //異常處理塊
  ...
}catch(SubException e2){
   //異常處理塊
   ...
}finally{
   //資源回收
   ...
}

  

例如:

public class Test {
	
	public static void main(String[] args) throws Exception {
		try {
			int a=10;
			int b=0;
			int c=a/b;
		}catch(Exception e) {
			System.out.println("未知異常");
		}finally {
			System.out.println("資源回收");
		}
	}

}

  

結果:

未知異常
資源回收

  

 

總結:

 

 

 

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!