01 . 容器編排簡介及Kubernetes核心概念

Kubernetes簡介

Kubernetes是谷歌嚴格保密十幾年的秘密武器—Borg的一個開源版本,是Docker分佈式系統解決方案.2014年由Google公司啟動.

Kubernetes提供了面嚮應用的容器集群部署和管理系統。Kubernetes的目標旨在消除編排物理/虛擬計算,網絡和存儲基礎設施的負擔,並使應用程序運營商和開發人員完全將重點放在以容器為中心的原語上進行自助運營。Kubernetes 也提供穩定、兼容的基礎(平台),用於構建定製化的workflows 和更高級的自動化任務。 Kubernetes 具備完善的集群管理能力,包括多層次的安全防護和准入機制、多租戶應用支撐能力、透明的服務註冊和服務發現機制、內建負載均衡器、故障發現和自我修復能力、服務滾動升級和在線擴容、可擴展的資源自動調度機制、多粒度的資源配額管理能力。 Kubernetes 還提供完善的管理工具,涵蓋開發、部署測試、運維監控等各個環節。

Kubernetes作為雲原生應用的基石,相當於一個雲操作系統,其重要性不言而喻。

容器編排

容器編排引擎三足鼎立:

    Mesos
    Docker Swarm+compose
    Kubernetes

早在 2015 年 5 月,Kubernetes 在 Google 上的搜索熱度就已經超過了 Mesos 和 Docker Swarm,從那兒之後更是一路飆升,將對手甩開了十幾條街,容器編排引擎領域的三足鼎立時代結束。

目前,AWS、Azure、Google、阿里雲、騰訊雲等主流公有雲提供的是基於 Kubernetes 的容器服務;Rancher、CoreOS、IBM、Mirantis、Oracle、Red Hat、VMWare 等無數廠商也在大力研發和推廣基於 Kubernetes 的容器 CaaS 或 PaaS 產品。可以說,Kubernetes 是當前容器行業最炙手可熱的明星。

Google 的數據中心裏運行着超過 20 億個容器,而且 Google 十年前就開始使用容器技術。

最初,Google 開發了一個叫 Borg 的系統(現在命名為 Omega)來調度如此龐大數量的容器和工作負載。在積累了這麼多年的經驗后,Google 決定重寫這個容器管理系統,並將其貢獻到開源社區,讓全世界都能受益。這個項目就是 Kubernetes。簡單的講,Kubernetes 是 Google Omega 的開源版本。

跟很多基礎設施領域先有工程實踐、後有方法論的發展路線不同,Kubernetes 項目的理論基礎則要比工程實踐走得靠前得多,這當然要歸功於 Google 公司在 2015 年 4 月發布的 Borg 論文了。

Borg 系統,一直以來都被譽為 Google 公司內部最強大的”秘密武器”。雖然略顯誇張,但這個說法倒不算是吹牛。

因為,相比於 Spanner、BigTable 等相對上層的項目,Borg 要承擔的責任,是承載 Google 公司整個基礎設施的核心依賴。在 Google 公司已經公開發表的基礎設施體系論文中,Borg 項目當仁不讓地位居整個基礎設施技術棧的最底層。

由於這樣的定位,Borg 可以說是 Google 最不可能開源的一個項目。而幸運地是,得益於 Docker 項目和容器技術的風靡,它卻終於得以以另一種方式與開源社區見面,這個方式就是 Kubernetes 項目。

所以,相比於”小打小鬧”的 Docker 公司、”舊瓶裝新酒”的 Mesos 社區,Kubernetes 項目從一開始就比較幸運地站上了一個他人難以企及的高度:在它的成長階段,這個項目每一個核心特性的提出,幾乎都脫胎於 Borg/Omega 系統的設計與經驗。更重要的是,這些特性在開源社區落地的過程中,又在整個社區的合力之下得到了極大的改進,修復了很多當年遺留在 Borg 體系中的缺陷和問題。

所以,儘管在發布之初被批評是”曲高和寡”,但是在逐漸覺察到 Docker 技術棧的”稚嫩”和 Mesos 社區的”老邁”之後,這個社區很快就明白了:k8s 項目在 Borg 體系的指導下,體現出了一種獨有的”先進性”與”完備性”,而這些特質才是一個基礎設施領域開源項目賴以生存的核心價值。

什麼是編排

一個正在運行的 Linux 容器,可以分成兩部分看待

1 . 容器的靜態視圖

一組聯合掛載在 /var/lib/docker/aufs/mnt 上的 rootfs,這一部分稱為”容器鏡像”(Container Image)

2 . 容器的動態視圖

一個由 Namespace+Cgroups 構成的隔離環境,這一部分稱為”容器運行時”(Container Runtime)

作為一名開發者,其實並不關心容器運行時的差異。在整個”開發 – 測試 – 發布”的流程中,真正承載着容器信息進行傳遞的,是容器鏡像,而不是容器運行時。

這正是容器技術圈在 Docker 項目成功后不久,就迅速走向了”容器編排”這個”上層建築”的主要原因:作為一家雲服務商或者基礎設施提供商,我只要能夠將用戶提交的 Docker 鏡像以容器的方式運行起來,就能成為這個非常熱鬧的容器生態圖上的一個承載點,從而將整個容器技術棧上的價值,沉澱在我的這個節點上。

更重要的是,只要從這個承載點向 Docker 鏡像製作者和使用者方向回溯,整條路徑上的各個服務節點,比如 CI/CD、監控、安全、網絡、存儲等等,都有可以發揮和盈利的餘地。這個邏輯,正是所有雲計算提供商如此熱衷於容器技術的重要原因:通過容器鏡像,它們可以和潛在用戶(即,開發者)直接關聯起來。

從一個開發者和單一的容器鏡像,到無數開發者和龐大的容器集群,容器技術實現了從”容器”到”容器雲”的飛躍,標志著它真正得到了市場和生態的認可。

這樣,容器就從一個開發者手裡的小工具,一躍成為了雲計算領域的絕對主角;而能夠定義容器組織和管理規範的”容器編排”技術,則當仁不讓地坐上了容器技術領域的”頭把交椅”。

最具代表性的容器編排工具

# 1. Docker 公司的 Compose+Swarm 組合
# 2. Google 與 RedHat 公司共同主導的 Kubernetes 項目

編排工具

Swarm與CoreOS

Docker 公司發布 Swarm 項目

Docker 公司在 2014 年發布 Swarm 項目. 一個有意思的事實:雖然通過”容器”這個概念完成了對經典 PaaS 項目的”降維打擊”,但是 Docker 項目和 Docker 公司,兜兜轉轉了一年多,卻還是回到了 PaaS 項目原本深耕多年的那個戰場:如何讓開發者把應用部署在我的項目上

Docker 項目從發布之初就全面發力,從技術、社區、商業、市場全方位爭取到的開發者群體,實際上是為此後吸引整個生態到自家”PaaS”上的一個鋪墊。只不過這時,”PaaS”的定義已經全然不是 Cloud Foundry 描述的那個樣子,而是變成了一套以 Docker 容器為技術核心,以 Docker 鏡像為打包標準的、全新的”容器化”思路。

這正是 Docker 項目從一開始悉心運作”容器化”理念和經營整個 Docker 生態的主要目的。

Docker 公司在 Docker 項目已經取得巨大成功后,執意要重新走回 PaaS 之路的原因:

雖然 Docker 項目備受追捧,但用戶們最終要部署的,還是他們的網站、服務、數據庫,甚至是雲計算業務。只有那些能夠為用戶提供平台層能力的工具,才會真正成為開發者們關心和願意付費的產品。而 Docker 項目這樣一個只能用來創建和啟停容器的小工具,最終只能充當這些平台項目的”幕後英雄”。

Docker 公司的老朋友和老對手 CoreOS:

CoreOS 是一個基礎設施領域創業公司。 核心產品是一個定製化的操作系統,用戶可以按照分佈式集群的方式,管理所有安裝了這個操作系統的節點。從而,用戶在集群里部署和管理應用就像使用單機一樣方便了。

Docker 項目發布后,CoreOS 公司很快就認識到可以把”容器”的概念無縫集成到自己的這套方案中,從而為用戶提供更高層次的 PaaS 能力。所以,CoreOS 很早就成了 Docker 項目的貢獻者,並在短時間內成為了 Docker 項目中第二重要的力量。

2014 年底,CoreOS 公司與 Docker 公司停止合作,並推出自己研製的 Rocket(後來叫 rkt)容器。

原因是 Docker 公司對 Docker 項目定位的不滿足。Docker 公司的解決方法是讓 Docker 項目提供更多的平台層能力,即向 PaaS 項目進化。這與 CoreOS 公司的核心產品和戰略發生了嚴重衝突。

Docker 公司在 2014 年就已經定好了平台化的發展方向,並且絕對不會跟 CoreOS 在平台層面開展任何合作。這樣看來,Docker 公司在 2014 年 12 月的 DockerCon 上發布 Swarm 的舉動,也就一點都不突然了。

CoreOS 項目

依託於一系列開源項目(比如 Container Linux 操作系統、Fleet 作業調度工具、systemd 進程管理和 rkt 容器),一層層搭建起來的平台產品

Swarm 項目:

以一個完整的整體來對外提供集群管理功能。Swarm 的最大亮點是它完全使用 Docker 項目原本的容器管理 API 來完成集群管理,比如:

單機 Docker 項目
docker run 我的容器

多機 Docker 項目

“docker run -H ” 我的 Swarm 集群 API 地址 ” ” 我的容器 “`

在部署了 Swarm 的多機環境下,用戶只需使用原先的 Docker 指令創建一個容器,這個請求就會被 Swarm 攔截下來處理,然後通過具體的調度算法找到一個合適的 Docker Daemon 運行起來。

這個操作方式簡潔明了,對於已經了解過 Docker 命令行的開發者們也很容易掌握。所以,這樣一個”原生”的 Docker 容器集群管理項目一經發布,就受到了已有 Docker 用戶群的熱捧。相比之下,CoreOS 的解決方案就顯得非常另類,更不用說用戶還要去接受完全讓人摸不着頭腦、新造的容器項目 rkt 了。

Swarm 項目只是 Docker 公司重新定義”PaaS”的關鍵一環。2014 年到 2015 年這段時間里,Docker 項目的迅速走紅催生出了一個非常繁榮的”Docker 生態”。在這個生態里,圍繞着 Docker 在各個層次進行集成和創新的項目層出不窮.

cncf(Fig/Compose)

Fig 項目

被docker收購后改名為 Compose

Fig 項目基本上只是靠兩個人全職開發和維護的,可它卻是當時 GitHub 上熱度堪比 Docker 項目的明星。

Fig 項目受歡迎的原因

是它在開發者面前第一次提出”容器編排”(Container Orchestration)的概念。

“編排”(Orchestration)在雲計算行業里不算是新詞彙,主要是指用戶如何通過某些工具或者配置來完成一組虛擬機以及關聯資源的定義、配置、創建、刪除等工作,然後由雲計算平台按照這些指定的邏輯來完成的過程。

容器時代,”編排”就是對 Docker 容器的一系列定義、配置和創建動作的管理。而 Fig 的工作實際上非常簡單:假如現在用戶需要部署的是應用容器 A、數據庫容器 B、負載均衡容器 C,那麼 Fig 就允許用戶把 A、B、C 三個容器定義在一個配置文件中,並且可以指定它們之間的關聯關係,比如容器 A 需要訪問數據庫容器 B。

接下來,只需執行一條非常簡單的指令:# fig up

Fig 就會把這些容器的定義和配置交給 Docker API 按照訪問邏輯依次創建,一系列容器就都啟動了;而容器 A 與 B 之間的關聯關係,也會交給 Docker 的 Link 功能通過寫入 hosts 文件的方式進行配置。更重要的是,你還可以在 Fig 的配置文件里定義各種容器的副本個數等編排參數,再加上 Swarm 的集群管理能力,一個活脫脫的 PaaS 呼之欲出。

它成了 Docker 公司到目前為止第二大受歡迎的項目,一直到今也依然被很多人使用。

當時的這個容器生態里,還有很多開源項目或公司。比如:

專門負責處理容器網絡的 SocketPlane 項目(後來被 Docker 公司收購)

專門負責處理容器存儲的 Flocker 項目(後來被 EMC 公司收購)

專門給 Docker 集群做圖形化管理界面和對外提供雲服務的 Tutum 項目(後來被 Docker 公司收購)等等。

Mesosphere與Mesos

老牌集群管理項目 Mesos 和它背後的創業公司 Mesosphere:Mesos 社區獨特的競爭力:

超大規模集群的管理經驗

Mesos 早已通過了萬台節點的驗證,2014 年之後又被廣泛使用在 eBay 等大型互聯網公司的生產環境中。

Mesos 是 Berkeley 主導的大數據套件之一,是大數據火熱時最受歡迎的資源管理項目,也是跟 Yarn 項目殺得難捨難分的實力派选手。

大數據所關注的計算密集型離線業務,其實並不像常規的 Web 服務那樣適合用容器進行託管和擴容,也沒有對應用打包的強烈需求,所以 Hadoop、Spark 等項目到現在也沒在容器技術上投下更大的賭注;

但對於 Mesos 來說,天生的兩層調度機制讓它非常容易從大數據領域抽身,轉而去支持受眾更加廣泛的 PaaS 業務。

在這種思路指導下,Mesosphere 公司發布了一個名為 Marathon 的項目,這個項目很快就成為 Docker Swarm 的一個有力競爭對手。

通過 Marathon 實現了諸如應用託管和負載均衡的 PaaS 功能之後,Mesos+Marathon 的組合實際上進化成了一個高度成熟的 PaaS 項目,同時還能很好地支持大數據業務。

Mesosphere 公司提出”DC/OS”(數據中心操作系統)的口號和產品:

旨在使用戶能夠像管理一台機器那樣管理一個萬級別的物理機集群,並且使用 Docker 容器在這個集群里自由地部署應用。這對很多大型企業來說具有着非同尋常的吸引力。

這時的容器技術生態, CoreOS 的 rkt 容器完全打不開局面,Fleet 集群管理項目更是少有人問津,CoreOS 完全被 Docker 公司壓制了。

RedHat 也是因為對 Docker 公司平台化戰略不滿而憤憤退出。但此時,它竟只剩下 OpenShift 這個跟 Cloud Foundry 同時代的經典 PaaS 一張牌可以打,跟 Docker Swarm 和轉型后的 Mesos 完全不在同一個”競技水平”之上。

google與k8s

2014 年 6 月,基礎設施領域的翹楚 Google 公司突然發力,正宣告了一個名叫 Kubernetes 項目的誕生。這個項目,不僅挽救了當時的 CoreOS 和 RedHat,還如同當年 Docker 項目的橫空出世一樣,再一次改變了整個容器市場的格局。

這段時間,也正是 Docker 生態創業公司們的春天,大量圍繞着 Docker 項目的網絡、存儲、監控、CI/CD,甚至 UI 項目紛紛出台,也湧現出了很多 Rancher、Tutum 這樣在開源與商業上均取得了巨大成功的創業公司。

在 2014~2015 年間,整個容器社區可謂熱鬧非凡。

這令人興奮的繁榮背後,卻浮現出了更多的擔憂。這其中最主要的負面情緒,是對 Docker 公司商業化戰略的種種顧慮。

事實上,很多從業者也都看得明白,Docker 項目此時已經成為 Docker 公司一個商業產品。而開源,只是 Docker 公司吸引開發者群體的一個重要手段。不過這麼多年來,開源社區的商業化其實都是類似的思路,無非是高不高調、心不心急的問題罷了。

而真正令大多數人不滿意的是,Docker 公司在 Docker 開源項目的發展上,始終保持着絕對的權威和發言權,並在多個場合用實際行動挑戰到了其他玩家(比如,CoreOS、RedHat,甚至谷歌和微軟)的切身利益。

那麼,這個時候,大家的不滿也就不再是在 GitHub 上發發牢騷這麼簡單了。

相信很多容器領域的老玩家們都聽說過,Docker 項目剛剛興起時,Google 也開源了一個在內部使用多年、經歷過生產環境驗證的 Linux 容器:lmctfy(Let Me Container That For You)。

然而,面對 Docker 項目的強勢崛起,這個對用戶沒那麼友好的 Google 容器項目根本沒有招架之力。所以,知難而退的 Google 公司,向 Docker 公司表示了合作的願望:關停這個項目,和 Docker 公司共同推進一个中立的容器運行時(container runtime)庫作為 Docker 項目的核心依賴。

不過,Docker 公司並沒有認同這個明顯會削弱自己地位的提議,還在不久后,自己發布了一個容器運行時庫 Libcontainer。這次匆忙的、由一家主導的、並帶有戰略性考量的重構,成了 Libcontainer 被社區長期詬病代碼可讀性差、可維護性不強的一個重要原因。

至此,Docker 公司在容器運行時層面上的強硬態度,以及 Docker 項目在高速迭代中表現出來的不穩定和頻繁變更的問題,開始讓社區叫苦不迭。

這種情緒在 2015 年達到了一個高潮,容器領域的其他幾位玩家開始商議”切割”Docker 項目的話語權。而”切割”的手段也非常經典,那就是成立一个中立的基金會。

於是,2015 年 6 月 22 日,由 Docker 公司牽頭,CoreOS、Google、RedHat 等公司共同宣布,Docker 公司將 Libcontainer 捐出,並改名為 RunC 項目,交由一個完全中立的基金會管理,然後以 RunC 為依據,大家共同制定一套容器和鏡像的標準和規範。

這套標準和規範,就是 OCI( Open Container Initiative )。OCI 的提出,意在將容器運行時和鏡像的實現從 Docker 項目中完全剝離出來。這樣做,一方面可以改善 Docker 公司在容器技術上一家獨大的現狀,另一方面也為其他玩家不依賴於 Docker 項目構建各自的平台層能力提供了可能。

不過,OCI 的成立更多的是這些容器玩家出於自身利益進行干涉的一個妥協結果。儘管 Docker 是 OCI 的發起者和創始成員,它卻很少在 OCI 的技術推進和標準制定等事務上扮演關鍵角色,也沒有動力去积極地推進這些所謂的標準。

這也是迄今為止 OCI 組織效率持續低下的根本原因。

OCI 並沒能改變 Docker 公司在容器領域一家獨大的現狀,Google 和 RedHat 等公司於是把第二把武器擺上了檯面。

Docker 之所以不擔心 OCI 的威脅,原因就在於它的 Docker 項目是容器生態的事實標準,而它所維護的 Docker 社區也足夠龐大。可是,一旦這場鬥爭被轉移到容器之上的平台層,或者說 PaaS 層,Docker 公司的競爭優勢便立刻捉襟見肘了。

在這個領域里,像 Google 和 RedHat 這樣的成熟公司,都擁有着深厚的技術積累;而像 CoreOS 這樣的創業公司,也擁有像 Etcd 這樣被廣泛使用的開源基礎設施項目。

可是 Docker 公司卻只有一個 Swarm。

所以這次,Google、RedHat 等開源基礎設施領域玩家們,共同牽頭髮起了一個名為 CNCF(Cloud Native Computing Foundation)的基金會。這個基金會的目的其實很容易理解:它希望,以 Kubernetes 項目為基礎,建立一個由開源基礎設施領域廠商主導的、按照獨立基金會方式運營的平台級社區,來對抗以 Docker 公司為核心的容器商業生態。

為了打造出一個圍繞 Kubernetes 項目的”護城河”,CNCF 社區就需要至少確保兩件事情:

# 1. Kubernetes 項目必須能夠在容器編排領域取得足夠大的競爭優勢
# 2. CNCF 社區必須以 Kubernetes 項目為核心,覆蓋足夠多的場景

CNCF 社區如何解決 Kubernetes 項目在編排領域的競爭力的問題:

在容器編排領域,Kubernetes 項目需要面對來自 Docker 公司和 Mesos 社區兩個方向的壓力。Swarm 和 Mesos 實際上分別從兩個不同的方向講出了自己最擅長的故事:Swarm 擅長的是跟 Docker 生態的無縫集成,而 Mesos 擅長的則是大規模集群的調度與管理。

這兩個方向,也是大多數人做容器集群管理項目時最容易想到的兩個出發點。也正因為如此,Kubernetes 項目如果繼續在這兩個方向上做文章恐怕就不太明智了。

Kubernetes 選擇的應對方式是:Borg

k8s 項目大多來自於 Borg 和 Omega 系統的內部特性,這些特性落到 k8s 項目上,就是 Pod、Sidecar 等功能和設計模式。

這就解釋了,為什麼 Kubernetes 發布后,很多人”抱怨”其設計思想過於”超前”的原因:Kubernetes 項目的基礎特性,並不是幾個工程師突然”拍腦袋”想出來的東西,而是 Google 公司在容器化基礎設施領域多年來實踐經驗的沉澱與升華。這正是 Kubernetes 項目能夠從一開始就避免同 Swarm 和 Mesos 社區同質化的重要手段。

CNCF 接下來的任務是如何把這些先進的思想通過技術手段在開源社區落地,並培育出一個認同這些理念的生態?

RedHat 發揮了重要作用。當時,Kubernetes 團隊規模很小,能夠投入的工程能力十分緊張,這恰恰是 RedHat 的長處。RedHat 更是世界上為數不多、能真正理解開源社區運作和項目研發真諦的合作夥伴。

RedHat 與 Google 聯盟的成立,不僅保證了 RedHat 在 Kubernetes 項目上的影響力,也正式開啟了容器編排領域”三國鼎立”的局面。

Mesos 社區與容器技術的關係,更像是”借勢”,而不是這個領域真正的參与者和領導者。這個事實,加上它所屬的 Apache 社區固有的封閉性,導致了 Mesos 社區雖然技術最為成熟,卻在容器編排領域鮮有創新。

一開始,Docker 公司就把應對 Kubernetes 項目的競爭擺在首要位置:
一方面,不斷強調”Docker Native”的”重要性”
一方面,與 k8s 項目在多個場合進行了直接的碰撞。

這次競爭的發展態勢,很快就超過了 Docker 公司的預期。

Kubernetes 項目並沒有跟 Swarm 項目展開同質化的競爭
所以 “Docker Native”的說辭並沒有太大的殺傷力
相反 k8s 項目讓人耳目一新的設計理念和號召力,很快就構建出了一個與眾不同的容器編排與管理的生態。

Kubernetes 項目在 GitHub 上的各項指標開始一騎絕塵,將 Swarm 項目遠遠地甩在了身後.

CNCF 社區如何解決第二個問題:

在已經囊括了容器監控事實標準的 Prometheus 項目后,CNCF 社區迅速在成員項目中添加了 Fluentd、OpenTracing、CNI 等一系列容器生態的知名工具和項目。

而在看到了 CNCF 社區對用戶表現出來的巨大吸引力之後,大量的公司和創業團隊也開始專門針對 CNCF 社區而非 Docker 公司制定推廣策略。

2016 年,Docker 公司宣布了一個震驚所有人的計劃:放棄現有的 Swarm 項目,將容器編排和集群管理功能全部內置到 Docker 項目當中。

Docker 公司意識到了 Swarm 項目目前唯一的競爭優勢,就是跟 Docker 項目的無縫集成。那麼,如何讓這種優勢最大化呢?那就是把 Swarm 內置到 Docker 項目當中。

從工程角度來看,這種做法的風險很大。內置容器編排、集群管理和負載均衡能力,固然可以使得 Docker 項目的邊界直接擴大到一個完整的 PaaS 項目的範疇,但這種變更帶來的技術複雜度和維護難度,長遠來看對 Docker 項目是不利的。

不過,在當時的大環境下,Docker 公司的選擇恐怕也帶有一絲孤注一擲的意味。

k8s 的應對策略

是反其道而行之,開始在整個社區推進”民主化”架構,即:從 API 到容器運行時的每一層,Kubernetes 項目都為開發者暴露出了可以擴展的插件機制,鼓勵用戶通過代碼的方式介入到 Kubernetes 項目的每一個階段。

Kubernetes 項目的這個變革的效果立竿見影,很快在整個容器社區中催生出了大量的、基於 Kubernetes API 和擴展接口的二次創新工作,比如:
目前熱度極高的微服務治理項目 Istio;
被廣泛採用的有狀態應用部署框架 Operator;
還有像 Rook 這樣的開源創業項目,它通過 Kubernetes 的可擴展接口,把 Ceph 這樣的重量級產品封裝成了簡單易用的容器存儲插件。

在鼓勵二次創新的整體氛圍當中,k8s 社區在 2016 年後得到了空前的發展。更重要的是,不同於之前局限於”打包、發布”這樣的 PaaS 化路線,這一次容器社區的繁榮,是一次完全以 Kubernetes 項目為核心的”百花爭鳴”。

面對 Kubernetes 社區的崛起和壯大,Docker 公司也不得不面對自己豪賭失敗的現實。但在早前拒絕了微軟的天價收購之後,Docker 公司實際上已經沒有什麼迴旋餘地,只能選擇逐步放棄開源社區而專註於自己的商業化轉型。

所以,從 2017 年開始,Docker 公司先是將 Docker 項目的容器運行時部分 Containerd 捐贈給 CNCF 社區,標志著 Docker 項目已經全面升級成為一個 PaaS 平台;緊接着,Docker 公司宣布將 Docker 項目改名為 Moby,然後交給社區自行維護,而 Docker 公司的商業產品將佔有 Docker 這個註冊商標。

Docker 公司這些舉措背後的含義非常明確:它將全面放棄在開源社區同 Kubernetes 生態的競爭,轉而專註於自己的商業業務,並且通過將 Docker 項目改名為 Moby 的舉動,將原本屬於 Docker 社區的用戶轉化成了自己的客戶。

2017 年 10 月,Docker 公司出人意料地宣布,將在自己的主打產品 Docker 企業版中內置 Kubernetes 項目,這標志著持續了近兩年之久的”編排之爭”至此落下帷幕。

2018 年 1 月 30 日,RedHat 宣布斥資 2.5 億美元收購 CoreOS。

2018 年 3 月 28 日,這一切紛爭的始作俑者,Docker 公司的 CTO Solomon Hykes 宣布辭職,曾經紛紛擾擾的容器技術圈子,到此塵埃落定。

容器技術圈子在短短几年裡發生了很多變數,但很多事情其實也都在情理之中。就像 Docker 這樣一家創業公司,在通過開源社區的運作取得了巨大的成功之後,就不得不面對來自整個雲計算產業的競爭和圍剿。而這個產業的垄斷特性,對於 Docker 這樣的技術型創業公司其實天生就不友好。

在這種局勢下,接受微軟的天價收購,在大多數人看來都是一個非常明智和實際的選擇。可是 Solomon Hykes 卻多少帶有一些理想主義的影子,既然不甘於”寄人籬下”,那他就必須帶領 Docker 公司去對抗來自整個雲計算產業的壓力。

只不過,Docker 公司最後選擇的對抗方式,是將開源項目與商業產品緊密綁定,打造了一個極端封閉的技術生態。而這,其實違背了 Docker 項目與開發者保持親密關係的初衷。相比之下,Kubernetes 社區,正是以一種更加溫和的方式,承接了 Docker 項目的未盡事業,即:以開發者為核心,構建一個相對民主和開放的容器生態。

這也是為何,Kubernetes 項目的成功其實是必然的。

很難想象如果 Docker 公司最初選擇了跟 Kubernetes 社區合作,如今的容器生態又將會是怎樣的一番景象。不過我們可以肯定的是,Docker 公司在過去五年裡的風雲變幻,以及 Solomon Hykes 本人的傳奇經歷,都已經在雲計算的長河中留下了濃墨重彩的一筆。

小結
# 1. 容器技術的興起源於 PaaS 技術的普及;
# 2. Docker 公司發布的 Docker 項目具有里程碑式的意義;
# 3. Docker 項目通過"容器鏡像",解決了應用打包這個根本性難題。

# 容器本身沒有價值,有價值的是"容器編排"。
# 也正因為如此,容器技術生態才爆發了一場關於"容器編排"的"戰爭"。而這次戰爭,最終以 Kubernetes 項目和 CNCF 社區的勝利而告終。

Kubernetes核心概念

什麼是Kubernetes?

Kubernetes是一個完備的分佈式系統支撐平台。

Kubernetes具有完備的集群管理能力,包括多層次的安全防護和准入機制/多租戶應用支撐能力、透明的服務註冊和服務發現機制、內建智能負載均衡器、強大的故障發現和自我修復功能、服務滾動升級和在線擴容能力、可擴展的資源自動調度機制,以及多粒度的資源配額管理能力。同時kubernetes提供了完善的管理工具,這些工具覆蓋了包括開發、測試部署、運維監控在內的各個環節;因此kubernetes是一個全新的基於容器技術的分佈式架構解決方案,並且是一個一站式的完備的分佈式系統開發和支撐平台.

Kubernetes Service介紹

Service是分佈式集群結構的核心,一個Server對象有以下關鍵特徵:

# 1. 擁有一個唯一指定的名字(比如mysql-server)
# 2. 擁有一個虛擬IP(Cluster IP,Service IP或VIP和端口號)
# 3. 能夠提供某種遠程服務能力
# 4. 被映射到了提供這種服務能力的一組容器應用上.

Service的服務進程目前都基於Socker通信方式對外提供服務,比如redis、memcache、MySQL、Web Server,或者是實現了某個具體業務的一個特定的TCP Server進程。雖然一個Service通常由多個相關的服務進程來提供服務,每個服務進程都有一個獨立的Endpoint(IP+Port)訪問點,但Kubernetes 能夠讓我們通過Service虛擬Cluster IP+Service Port連接到指定的Service上。有了Kubernetes內建的透明負載均衡和故障恢復機制,不管後端有多少服務進程,也不管某個服務進程是否會由於發生故障而重新部署到其他機器,都不會影響到我們對服務的正常調用。更重要的是這個Service本身一旦創建就不再變化,這意味着Kubernetes集群中,我們再也不用為了服務的IP地址變來變去的問題而頭疼。

Kubernetes Pod介紹

Pod概念 Pod運行在一個我們稱之為Node的環境中,可以是私有雲也可以是公有雲的虛擬機或者物理機上,通常在一個節點上運行幾百個Pod,每個Pod運行着一個特殊的稱之為Pause的容器,其他容器則為業務容器,這些業務容器共享着Pause容器的網絡棧和Volume掛載卷,因此他們之間的通訊和數據交換更為高效,在設計時我們充分利用這一特徵將一組密切相關的服務進程放入同一個Pod中.

並不是每個Pod和它裏面的容器都映射到一個Service上,只是那些提供服務(無論是內還是對外)的一組Pod才會被映射成一個服務.

Service和Pod如何關聯

容器提供了強大的隔離功能,所以有必要把Service提供服務的這組容器放入到容器中隔離,Kubernetes設計了Pod服務,將每個服務進程包裝成相應的Pod中,使其成為Pod中運行的一個容器Container,為了建立Service和Pod間的關聯關係,Kubernetes首先給每個Pod貼上了一個標籤Label,給運行Mysql的Pod貼上了name=mysql標籤,給運行PHP貼上name=php標籤,然後給相應的Service定義標籤選擇器Label Selector,比如Mysql Service的標籤選擇器選擇條件為name=mysql,意為該Service要作用於所有包含name=mysql Label的Pod上,這樣就巧妙的解決了Service和Pod關聯的問題.

Kubernetes RC介紹

RC介紹在Kubernetes集群中,你只需要為需要擴容的Service關聯的Pod創建一個RC(Replication Controller),則該Service的擴容以至於後來的Service升級等頭疼問題都可以迎刃而解,定義一個RC文件包含以下3個關鍵點.

# 1. 目標Pod的定義
# 2. 目標Pod需要運行的副本數量(Replicas)
# 3. 要監控的目標Pod的標籤(Label)

在創建好RC系統自動創建號Pod后,Kubernetes會通過RC中定義的Label篩選出對應的Pod實例並監控其狀態和數量,如果實例數量少於定義的副本數量Replicas則會用RC中定義的Pod模板來創建一個新的Pod,然後將Pod調度到合適的Node上運行,直到Pod實例的數量達到預定目標,這個過程完全是自動化的,無需人干預,只需要修改RC中的副本數量即可.

Kubernetes Master介紹

Kubernetes 里的Master指的是集群控制節點,每個Kubernetes集群里需要有一個Master節點來負責整個集群的管理和控制,基本上Kubernetes所有的控制命令都發給它,它負責具體的執行過程,我們後面執行的所有命令基本上都是在Master節點上運行的。如果Master宕機或不可用,那麼集群內容器的管理都將失效.

Master節點上運行一下一組關鍵進程:

  1. Kubernetes API Server: 提供了HTTP Rest接口的關鍵服務進程,是Kubernetes里所有資源的增刪改查等操作的唯一入口,也是集群控制的入門進程.
  2. Kubernetes Controller Manager 里所有的資源對象的自動化控制中心.
  3. Kubernetes Scheduler: 負責資源調度(Pod調度)的進程

另外在Master節點還需要啟動一個etcd服務,因為Kubernetes里所有資源對象的數據全部保存在etcd中.

Kubernetes Node介紹

除了Master,集群中其他機器稱為Node節點,每個Node都會被分配一些工作負載Docker容器,當某個Node宕機,其上的工作負載都會被Master自動轉移到其他節點上去.

每個Node節點上都運行着以下一組關鍵進程

# 1. kubelet: 負責Pod對應的創建、停止等服務,同時與Master節點密切協作,實現集群管理的基本功能.
# 2. kube-proxy: 實現Kubernetes Service的通信與負載均衡機制的重要組件.
# 3. Docker Engine: Docker引擎,負責本機的容器創建和管理工作

在集群管理方面,Kubernetes將集群中的機器劃分為一個Master節點和一群工作節點(Node)中,在Master節點上運行着集群管理相關的一組進程kube-apiserver,kube-controller-manager和kube-scheduler,這些進程實現了整個集群的資源管理,Pod調度,彈性伸縮,安全控制,系統監控和糾錯等管理功能,並且都是全自動完成的、Node作為集群中的工作節點,運行真正的應用程序,在Node上Kubernetes最小運行單元是Pod,Node上運行着Kubernetes的Kubelet、kube-proxy服務進程,這些服務進程負責Pod創建、啟動、監控、重啟、銷毀以及軟件模式的負載均衡.

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

門多西諾複合大火續燒 面積相當於洛杉磯市

摘錄自2018年8月8日中央通訊社台北報導

美國加州野火繼續傳出災情,「門多西諾複合大火」延燒面積已相當於整個洛杉磯市,上萬名消防員今天仍不斷努力救災控制火勢,上月爆發的一連串野火已造成11人喪命。

加州北部的門多西諾複合大火(Mendocino Complex Fire)由2個分別名為「河流」(River fire)及「牧場」(Ranch fire)的野火組成,延燒面積已達29萬英畝(11萬7359公頃),大小相當於整個洛杉磯市。

門多西諾複合大火昨天(7日)改寫短短8個月前「湯瑪斯野火」(Thomas Fire)紀錄,成為加州有紀錄以來最大野火。湯瑪斯野火去年12月在南加州肆虐,延燒面積達28萬1893英畝。

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

【其他文章推薦】

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

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

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

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

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

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

蓄意污染亞馬遜雨林 雪佛龍在厄瓜多憲法法庭敗訴

環境資訊中心外電;姜唯 翻譯;林大利 審校;稿源:ENS

美國雪佛龍(Chevron)公司的世紀污染訴訟案,有了最新進展。厄瓜多憲法法庭稍早駁回了雪佛龍對95億美元污染判決的最終上訴,認定該公司蓄意將數十億加侖的有毒油廢料傾倒在亞馬遜熱帶雨林的原民土地上。

長達151頁的判決書中,以8比0一致判定原告原民團體勝訴,並駁回了雪佛龍的所有說法,例如雪佛龍自己也是詐欺受害者,以及厄瓜多法院對此案無管轄權等都遭到駁回。

前油田運營商德士古(後被雪佛龍收購)蓄意將數十億加侖的有毒油廢料傾倒在亞馬遜熱帶雨林。圖片來源:Caroline Bennett / Rainforest Action Network

環境集體訴訟案 第四次獲勝

厄瓜多憲法法院僅審理憲法問題,是厄瓜多第三大上訴法院,也是該國第四個維持2011年判決結果的法院,連同厄瓜多最高民事法庭和國家法院一致裁定雪佛龍須做出鉅額賠償。

這個針對雪佛龍公司的集體訴訟案,判決書於7月11日發布,原告是守護亞馬遜陣線(Frente de Defensa de la Amazonia),代表厄瓜多亞馬遜北部地區80個原民和農民社區的草根團體。

該訴訟最初由約3萬名熱帶雨林村民於1993年在紐約聯邦法院提出,被告是前油田運營商德士古(Texaco,已被雪佛龍公司收購)。2001年雪佛龍公司接受厄瓜多具有管轄權後,一名美國聯邦法官將此案移交給厄瓜多法院。

「這個判決是厄瓜多人民20年來為了爭取環境正義,對抗世界級污染者、流氓企業的再一次巨大勝利,」環保金人獎得主、1993年訴訟案起草者楊澤(Luis Yanza)表示。他目前也是守護亞馬遜陣線的主席。

楊澤說:「在雪佛龍開始賠償厄瓜多人民前,任何國家都不應該與雪佛龍做生意。」

致癌油廢料污染 雪佛龍八年拖延纏訟

厄瓜多首席律師薩拉查(Patricio Salazar)表示,「司法已認定雪佛龍非法攻擊代表原住民社群的律師,而不是根據是非來訴訟。雪佛龍現在極有可能必須全額支付賠償金,因為它在法律上和道德上都有義務這樣做。」

厄瓜多初審法院依據105份技術證據報告,在2011年判決該公司致癌油廢料污染了1500平方英里的亞馬遜土地。但是八年來,雪佛龍以拖延戰術因應訴訟。

生活在熱帶雨林中的數千人,包括許多原住民,已經死於癌症,而成千上萬的人必須忍受這場世界級的公衛災難。

代表厄瓜多村民的美國律師唐齊格(Steven Donziger)。圖片來源:Steven Donziger

拒支付賠償金 村民仍沒有醫療

收受雪佛龍公司400口油井特許使用費的厄瓜多政府,沒有給受害者太大幫助。

哈佛大學畢業,代表厄瓜多村民的美國律師唐齊格(Steven Donziger)曾走訪受影響地區數十次,他表示受影響地區並沒有醫療服務,許多人甚至一次醫生都沒看,也沒接受任何治療就死於癌症。

「雪佛龍在厄瓜多引發了一場史無前例的人道主義危機。再不清除污染,未來幾年將有數萬人死亡。全世界都必須關注,雪佛龍的股東和管理層必須立即採取行動,解決這個日益嚴重的問題。」唐齊格說。

雪佛龍多年來一直拒絕支付判決賠償金,目前連本帶利已經來到120億美元(約新台幣1361億元)。

雪佛龍公司人員甚至威脅原民,若繼續堅持訴訟,他們可和原告「終身纏訟」。

卡普蘭法官 採信偽證風波

這份最新判決也是對美國紐約南區地方法院法官卡普蘭(Lewis A. Kaplan)的重大打擊。

2014年,這位具有爭議性的法官僅憑一位承認收賄的雪佛龍證人的假證詞便裁定,厄瓜多最高法院對雪佛龍提出的95億美元賠償判決是透過欺詐和脅迫手段取得。卡普蘭拒絕讓公正的事實調查員陪審團參與,也拒絕考量任何有關雪佛龍污染厄瓜多環境的證據。

但新事證顯示,雪佛龍支付厄瓜多前法官格拉(Alberto Guerra)大筆賄款之後,卡普蘭的判決被推翻。雪佛龍幫助格拉一家搬到美國後,格拉承認在雪佛龍律師的指導下作偽證。

卡普蘭僅憑格拉的偽證做出判決,也是全世界唯一做出有利雪佛龍判決的法官。

「出於省錢的錯誤營運決策」 法庭定調蓄意污染

基於比卡普蘭所能取得更完整的證據,17名厄瓜多法官作出有利厄瓜多村民的判決。12名加拿大法官,包括該國最高法院,也針對多個技術性問題作出有利厄瓜多村民的判決。

厄瓜多憲法法庭強調,雪佛龍污染環境造成嚴重後果不是意外造成,而是處心積慮為公司和股東省錢的運營決策,加上20年來蓄意拖延的額外罪行所導致。

厄瓜多原告也在加拿大法院取得了幾項上訴勝利,他們在加國蒐集雪佛龍資產,以迫使其遵守厄瓜多的判決。

Chevron Defeated in Ecuador’s Constitutional Court QUITO, Ecuador, July 31, 2018 (ENS)

In a benchmark pollution case, Ecuador’s Constitutional Court has rejected Chevron’s final appeal of a $9.5 billion pollution judgment that found the company deliberately dumped billions of gallons of toxic oil waste onto Indigenous lands in the Amazon rainforest.

Photo: Caroline Bennett / Rainforest Action Network

The unanimous 8-0 decision, issued in a 151-page document published July 11, was a total victory for the Indigenous groups that brought the case and a rejection of all of Chevron’s claims.

The Court rejected Chevron’s allegations that it was victimized by fraud, and the court threw out the company’s claim that Ecuadorian courts had no jurisdiction over the matter.

Ecuador’s Constitutional Court, which deals only with Constitutional issues, is the third major appellate court in Ecuador and the fourth court overall in the country to uphold the trial-level decision against Chevron, which was issued in 2011. Ecuador’s highest civil court, the National Court of Justice, has ruled unanimously to affirm the judgment against Chevron.

The class action case against Chevron was spearheaded by the Frente de Defensa de la Amazonia, the Amazon Defense Front, a grassroots group representing 80 Indigenous peoples and farmer communities in Ecuador’s northern Amazon region.

The case was originally filed in 1993 in federal court in New York against the former oil field operator Texaco, now part of Chevron Corporation, on behalf of an estimated 30,000 rainforest villagers. But in 2001 a U.S. federal judge moved it to Ecuador’s courts at Chevron’s request after the company accepted jurisdiction there.

“This decision is another huge victory for the people of Ecuador in their historic two-decade battle for environmental justice against the world’s worst corporate polluter and rogue operator,” said Luis Yanza, a Goldman Prize winner who initiated the lawsuit against Chevron in U.S. federal court in 1993, and serves as president of the Frente de Defensa de la Amazonia, Amazon Defense Front.

“No country should ever do business with Chevron until the company first pays for the harm it caused to the people of Ecuador,” Yanza said.

Patricio Salazar, the lead Ecuadorian lawyer on the case, said, “Justice has prevailed over Chevron’s illegal attempts to engage in constant attacks on lawyers who defend the Indigenous communities rather than litigate in good faith on the merits. It is now highly likely that Chevron will pay every last dollar of the judgment against it, as it is legally and ethically obligated to do.”

After eight years of proceedings slowed by Chevron’s strategy of deliberate delay, Ecuador’s trial court relied on 105 technical evidentiary reports to find in 2011 that the company poisoned a 1,500 square mile area of the Amazon with carcinogenic oil waste.

Thousands who live in the rainforest, including many Indigenous peoples, have died of cancer while tens of thousands must endure what is one of the world’s worst ongoing public health catastrophes.

Photo: Jonathan McIntosh / Rainforest Action Network

Ecuador’s government, which received royalties from Chevron’s operation of 400 well sites, has been of little help to the victims. Medical care in the affected region is non-existent, and many people perish from cancer without even visiting a doctor and after receiving no treatment, said Steven Donziger, the Harvard educated U.S. legal representative of the Ecuadorian communities, who has taken dozens of trips to the affected area.

“Chevron has caused a humanitarian crisis in Ecuador of epic proportions that is ongoing to this day,” he said. “Tens of thousands of people will die in the coming years if nothing is done to clean up the pollution. The world must pay attention and Chevron shareholders and management must act immediately to address this worsening problem.”

Chevron has refused for years to pay the Ecuador judgment, now worth $12 billion with interest. Company officials have threatened the Indigenous groups with a “lifetime of litigation” if they persist.

The latest Ecuadorean court decision is also a major blow to controversial Judge Lewis A. Kaplan, a judge of the U.S. District Court for the Southern District of New York.

In 2014, Judge Kaplan ruled that the $9.5 billion Lago Agrio judgment leveled against Chevron by Ecuador’s highest court, was obtained by way of fraud and coercion.

Kaplan relied on false testimony from an admittedly corrupt Chevron witness to find that the Ecuador judgment was procured by fraud. Kaplan refused to seat a jury of impartial fact finders, and he refused to consider any evidence of Chevron’s environmental contamination in Ecuador.

But Kaplan’s decision was disproven after evidence emerged that Chevron paid large sums to Alberto Guerra, a former Ecuadorian judge booted from the bench after he admitted taking bribes. Guerra was moved with his family by Chevron to the United States and later admitted lying on the stand after being coached for 53 days by Chevron lawyers headed by Randy Mastro at Gibson Dunn.

Kaplan based his core findings largely on Guerra’s false testimony. And Kaplan remains the only judge in the world to have ruled in favor of Chevron.

Seventeen Ecuador judges, who had access to a fuller evidentiary record than Kaplan, ruled in favor of the affected communities. Twelve judges from Canada, including the country’s entire Supreme Court, have also ruled in favor of the Ecuadorians on various technical issues.

The Ecuador decision confronts Chevron on the brutal human consequences of both its original environmental crimes, which the Court emphasizes were not the result of an accident, but rather of deliberate operational decision-making designed to save money and enrich the company’s shareholders and executives, and the additional offense of its two-decade campaign of distraction and delay.

The Ecuadorian plaintiffs also have picked up several appellate victories in Canadian courts as they attempt to collect Chevron assets in that country to force compliance with the Ecuador judgment.

※ 全文及圖片詳見:

作者

如果有一件事是重要的,如果能為孩子實現一個願望,那就是人類與大自然和諧共存。

於特有生物研究保育中心服務,小鳥和棲地是主要的研究對象。是龜毛的讀者,認為龜毛是探索世界的美德。

延伸閱讀

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

【其他文章推薦】

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

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

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

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

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

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

野花如何用多樣性抗氣候變遷 基因研究說分明

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

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

【其他文章推薦】

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

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

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

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

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

※回頭車貨運收費標準

不能放任蜜蜂死 法國禁五款類尼古丁農藥

摘錄自2018年8月10日中央社報導

法國政府為遏止蜜蜂持續大量死亡,公告自9月1日起禁用五種含類尼古丁的農藥。

法國於2016年實施生物多樣性法規,當時就決定要禁用含類尼古丁(neonicotinoid)的殺蟲劑,這種物質會破壞昆蟲的神經系統,導致蜜蜂等花粉媒介昆蟲迷路及死亡,威脅到生物多樣性和糧食生產。

法國這一波禁用的五種殺蟲劑包括益達胺(imidacloprid)、可尼丁(clothianidin)、賽速安(Thiamethoxam)、賽果培(Thiacloprid)和亞滅培(Acetamiprid)。

法國政府對這類殺蟲劑的禁令比歐盟嚴格。費加洛報(Le Figaro)報導,法國生態部表示,國會正在審議糧食法草案,通過後,應可再增加禁用類尼古丁殺蟲劑的種類。

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

美法院裁決 環保署須下令禁用陶斯松

摘錄自2018年8月10日中央社報導

聯邦第九巡迴上訴法院今天(10日)裁決,美國環境保護署(EPA)須在60天內下令禁用陶斯松(chlorpyrifos)。批評人士指出,這種廣泛使用的農藥會傷害兒童和農民。

第九巡迴上訴法院以2票贊成、1票反對的裁決結果,推翻前環保署長普魯特(Scott Pruitt)2017年3月拒絕接受環保團體請願的決定。當時環保團體呼籲,禁止陶斯松用於水果、蔬菜和堅果等糧食作物。

代表第九巡迴上訴法院撰寫裁決書的法官拉柯夫(Jed Rakoff)指出,「科學證據顯示,殘留在糧食上(的陶斯松)會對兒童神經發育造成損害」,但環保署未能提出有力反駁,因此下令環保署須在60天內下令禁用陶斯松。

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

【其他文章推薦】

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

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

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

SourceTree使用詳解(連接遠程倉庫,克隆,拉取,提交,推送,新建/切換/合併分支,衝突解決)

前言:

  俗話說的好工欲善其事必先利其器,Git分佈式版本控制系統是我們日常開發中不可或缺的。目前市面上比較流行的Git可視化管理工具有SourceTree、Github Desktop、TortoiseGit,綜合網上的一些文章分析和自己的日常開發實踐心得個人比較推薦開發者使用SourceTree,因為SourceTree同時支持Windows和Mac,並且界面十分的精美簡潔,大大的簡化了開發者與代碼庫之間的Git操作方式。該篇文章主要是對日常開發中使用SourceTree可視化管理工具的一些常用操作進行詳細講解。

SourceTree | Github Desktop | TortoiseGit 可視化管理工具對比:

  https://blog.csdn.net/hmllittlekoi/article/details/104504406/

SourceTree介紹和Atlassian賬號註冊和登錄教程:

https://www.cnblogs.com/Can-daydayup/p/13128511.html

連接Gitee or GitHub,獲取代碼:

注意:這裏介紹的是使用SSH協議獲取關聯遠程倉庫的代碼,大家也可以直接使用過HTTPS協議的方式直接輸入賬號密碼獲取關聯代碼!

全面概述Gitee和GitHub生成/添加SSH公鑰:

https://www.cnblogs.com/Can-daydayup/p/13063280.html

在SourceTree中添加SSH密鑰:

工具=>選擇:

   
添加SSH密鑰位置:C:\Users\xxxxx\.ssh\id_rsa.pub:

SSH客戶端選擇OpenSSH:

 

Clone對應託管平台倉庫(以Gitee為例):

打開碼雲,找到自己需要Clone的倉庫!

 

 

SourceTree設置默認工作目錄:

  由上面我們可以發現每次Clone克隆項目的時候,克隆下來的項目默認存儲位置都是在C盤,因此每次都需要我們去選擇項目存放的路徑,作為一個喜歡偷懶的人而言當然不喜歡這種方式啦,因此我們可以設置一個默認的項目存儲位置。

設置SourceTree默認項目目錄:

點擊工具=>選項=>一般=>找到項目目錄設置Clone項目默認存儲的位置  

SourceTree代碼提交:

1.首先切換到需要修改功能代碼所在的分支:

 

 

2.將修改的代碼提交到暫存區:

3.將暫存區中的代碼提交到本地代碼倉庫:

注意:多人同時開發項目的時候,不推薦默認選中立即推送變更到origin/develop,避免一些不必要的麻煩!

 4.代碼拉取更新本地代碼庫,並將代碼推送到遠程倉庫:

 
 勾選需要推送的分支,點擊推送到遠程分支:  
代碼成功推送到遠程代碼庫:

5.在Gitee中查看推送結果:

SourceTree分支切換,新建,合併:

1.分支切換:

雙擊切換:   單擊鼠標右鍵切換:

2.新建分支:

注意:在新建分支時,我們需要在哪個主分支的基礎上新建分支必須先要切換到對應的主分支才能到該主分支上創建分支,如下我們要在master分支上創建一個feature-0613分支:  

3.合併分支:

注意:在合併代碼之前我們都需要將需要合併的分支拉取到最新狀態(**避免覆蓋別人的代碼,或者丟失一些重要文件)!!!!!   在master分支上點擊右鍵,選擇合併feature-0613至當前分支即可進行合併:   分支合併成功:

SourceTree代碼衝突解決:

首先我們需要製造一個提交文件遇到衝突的情景:

在SoureceTree中在Clone一個新項目,命名為pingrixuexilianxi2,如下圖所示:

 

 

我們以項目中的【代碼合併衝突測試.txt】文件為例:   在pingrixuexilianxi2中添加內容,並提交到遠程代碼庫,添加的內容如下:   在pingrixuexilianxi中添加內容,提交代碼(不選擇立即推送變更到origin/master),拉取代碼即會遇到衝突:  

 

 

  衝突文件中的內容:  

直接打開衝突文件手動解決衝突:

由下面的衝突文件中的衝突內容我們了解到:

<<<<<<< HEAD
6月19日 pingrixuexilianxi添加了內容
=======
6月18日 pingrixuexilianxi2修改了這個文件哦
>>>>>>> a8284fd41903c54212d1105a6feb6c57292e07b5

  <<<<<<< HEAD到 =======裏面的【6月19日 pingrixuexilianxi添加了內容】是自己剛才的Commit提交的內容 =======到 >>>>>>> a8284fd41903c54212d1105a6feb6c57292e07b5裏面的【6月18日 pingrixuexilianxi2修改了這個文件哦】是遠程代碼庫更新的內容(即為pingrixuexilianxi2本地代碼庫推送修改內容)。   手動衝突解決方法:

  根據項目需求刪除不需要的代碼就行了,假如都需要的話我們只需要把 <<<<<<< HEAD=======     >>>>>>> a8284fd41903c54212d1105a6feb6c57292e07b5都刪掉衝突就解決了(注意,在項目中最後這些符號都不能存在,否則可能會報異常)。

  最後將衝突文件標記為已解決,提交到遠程倉庫:  

採用外部文本文件對比工具Beyond Compare解決衝突:

SourceTree配置文本文件對比工具Beyond Compare:

工具=>選項=>比較:  

 

使用Beyond Compare解決衝突:

Beyond Compare使用技巧: 官方全面教程: https://www.beyondcompare.cc/jiqiao/   SourceTree打開外部和合併工具:

 

注意:第一次啟動Beynod Compare軟件需要一會時間,請耐心等待:
    Beynod Compare進行衝突合併:   點擊保存文件后關閉Beynod Compare工具,SourceTree中的衝突就解決了,在SourceTree中我們會發現多了一個 .orig 的文件。接着選中那個.orig文件,單擊右鍵 => 移除,最後我們推送到遠程代碼庫即可:  

Sourcetree中的基本名詞說明:

克隆/新建(clone):從遠程倉庫URL加載創建一個與遠程倉庫一樣的本地倉庫。 提交(commit):將暫存區文件上傳到本地代碼倉庫。
推送(push):將本地倉庫同步至遠程倉庫,一般推送(push)前先拉取(pull)一次,確保一致(十分注意:這樣你才能達到和別人最新代碼同步的狀態,同時也能夠規避很多不必要的問題)。 拉取(pull):從遠程倉庫獲取信息並同步至本地倉庫,並且自動執行合併(merge)操作(git pull=git fetch+git merge)。 獲取(fetch):從遠程倉庫獲取信息並同步至本地倉庫。 分支(branch):創建/修改/刪除分枝。 合併(merge):將多個同名文件合併為一個文件,該文件包含多個同名文件的所有內容,相同內容抵消。 貯藏(git stash):保存工作現場。 丟棄(Discard):丟棄更改,恢復文件改動/重置所有改動,即將已暫存的文件丟回未暫存的文件。 標籤(tag):給項目增添標籤。 工作流(Git Flow):團隊工作時,每個人創建屬於自己的分枝(branch),確定無誤后提交到master分支。 終端(terminal):可以輸入git命令行。 每次拉取和推送的時候不用每次輸入密碼的命令行:git config credential.helper osxkeychain sourcetree。 檢出(checkout):切換不同分支。 添加(add):添加文件到緩存區。 移除(remove):移除文件至緩存區。 重置(reset):回到最近添加(add)/提交(commit)狀態。

Git分佈式版本控制器常用命令和使用:

當然作為一個有逼格的程序員, 一些常用的命令我們還是需要了解和掌握的,詳情可參考我之前寫過的文章:

https://www.cnblogs.com/Can-daydayup/p/10134733.html

 

 

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

【其他文章推薦】

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

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

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

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

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

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

八張圖徹底了解JDK8 GC調優秘籍-附PDF下載

目錄

  • 簡介
  • 分代垃圾回收器的內存結構
  • JDK8中可用的GC
  • 打印GC信息
  • 內存調整參數
  • Thread配置
  • 通用GC參數
  • CMS GC
  • G1參數
  • 總結

簡介

JVM的參數有很多很多,根據我的統計JDK8中JVM的參數總共有1853個,正式的參數也有680個。

這麼多參數帶給我們的是對JVM的細粒度的控制,但是並不是所有的參數都需要我們自己去調節的,我們需要關注的是一些最常用的,對性能影響比較大的GC參數即可。

為了更好的讓大家理解JDK8中 GC的調優的秘籍,這裏特意準備了八張圖。在本文的最後,還附帶了一個總結的PDF all in one文檔,大家把PDF下載回去,遇到問題就看兩眼,不美嗎?

分代垃圾回收器的內存結構

為了更好的提升GC的效率,現代的JVM都是採用的分代垃圾回收的策略(ZGC不是)。

java運行時內存可以分為JVM內存和非JVM內存。

JVM內存又可以分為堆內存和非堆內存。

堆內存大家都很熟悉了,YoungGen中的Eden,Survivor和OldGen。

非堆內存中存儲的有thread Stack,Code Cache, NIO Direct Buffers,Metaspace等。

注意這裏的Metaspace元空間是方法區在JDK8的實現,它是在本地內存中分配的。

JDK8中可用的GC

JDK8中到底有哪些可以使用的GC呢?

這裏我們以HotSpot JVM為例,總共可以使用4大GC方式:

其中對於ParallelGC和CMS GC又可以對年輕代和老年代分別設置GC方式。

大家看到上圖可能有一個疑問,Parallel scavenge和Parallel有什麼區別呢?

其實這兩個GC的算法是類似的,Parallel Scavenge收集器也經常稱為“吞吐量優先”收集器,Parallel Scavenge收集器提供了兩個參數用於精確控制吞吐量; -XX:MaxGCPauseMillis:控制最大垃圾收集停頓時間; -XX:GCTimeRatio:設置吞吐量大小。

同時Parallel Scavenge收集器能夠配合自適應調節策略,把內存管理的調優任務交給虛擬機去完成。

JDK8中默認開啟的是ParallelGC。

打印GC信息

如果想研究和理解GC的內部信息,GC信息打印是少不了的:

上圖提供了一些非常有用的GC日誌的控制參數。

內存調整參數

JVM分為Heap區和非Heap區,各個區又有更細的劃分,下面就是調整各個區域大小的參數:

Thread配置

TLAB大家還記得嗎?TLAB的全稱是Thread-Local Allocation Buffers。TLAB是在Eden區間分配的一個一個的連續空間。然後將這些連續的空間分配個各個線程使用。

因為每一個線程都有自己的獨立空間,所以這裏不涉及到同步的概念。

上圖就是TLAB的參數。

通用GC參數

雖然JDK8的GC這麼多,但是他們有一些通用的GC參數:

這裏講解一下Young space tenuring,怎麼翻譯我不是很清楚,這個主要就是指Young space中的對象經過多少次GC之後會被提升到Old space中。

CMS GC

CMS全稱是Concurrent mark sweep。是一個非常非常複雜的GC。

複雜到什麼程度呢?光光是CMS調優的參數都有一百多個!

下圖是常用的CMS的參數。

CMS這裏就不多講了,因為在JDK9之後,CMS就已經被廢棄了。

主要原因是CMS太過複雜,如果要向下兼容需要巨大的工作量,然後就直接被廢棄了。

在JDK9之後,默認的GC是G1。

G1參數

G1收集器是分代的和region化的,也就是整個堆內存被分為一系列大小相等的region。在啟動時,JVM設置region的大小,根據堆大小的不同,region的大小可以在1MB到32MB之間變動,region的數量最多不超過2048個。Eden區、Survivor區、老年代是這些region的邏輯集合,它們並不是連續的。

G1中的垃圾收集過程:年輕代收集和混合收集交替進行,背後有全局的併發標記周期在進行。當老年代分區佔用的空間達到或超過初始閾值,就會觸發併發標記周期。

下圖是G1的調優參數:

總結

上面總共8副圖,我把他們做成了一個PDF,預覽界面大概是這樣子的:

大家可以通過下面的鏈接直接下載PDF版本:

JDK8GC-cheatsheet.pdf

如果遇到問題可以直接拿過來參考。這種東西英文名字應該叫JDK8 GC cheatsheet,翻譯成中文應該就是JDK8 GC調優秘籍!

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/jdk8-gc-cheatsheet/

本文來源:flydean的博客

歡迎關注我的公眾號:程序那些事,更多精彩等着您!

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

【其他文章推薦】

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

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

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

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

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

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

Newtonsoft 六個超簡單又實用的特性,值得一試 【下篇】

一:講故事

上一篇介紹的 6 個特性從園子里的反饋來看效果不錯,那這一篇就再帶來 6 個特性同大家一起欣賞。

二:特性分析

1. 像弱類型語言一樣解析 json

大家都知道弱類型的語言有很多,如: nodejs,python,php,它們有一個的地方就是處理json,不需要像 強類型語言 那樣還要給它配一個類,什麼意思呢? 就拿下面的 json 說事。


{
  "DisplayName": "新一代算法模型",
  "CustomerType": 1,
  "Report": {
    "TotalCustomerCount": 1000,
    "TotalTradeCount": 50
  },
  "CustomerIDHash": [1,2,3,4,5]
}

這個 json 如果想灌到 C# 中處理,你就得給它定義一個適配的類,就如 初篇 的客戶算法模型類,所以這裏就有了一個需求,能不能不定義類也可以自由解析上面這串 json 呢??? 哈哈,當然是可以的, 反序列化成 Dictionary 即可,就拿提取 Report.TotalCustomerCountCustomerIDHash 這兩個字段演示一下。


        static void Main(string[] args)
        {
            var json = @"{
                           'DisplayName': '新一代算法模型',
                           'CustomerType': 1,
                           'Report': {
                             'TotalCustomerCount': 1000,
                             'TotalTradeCount': 50
                           },
                           'CustomerIDHash': [1,2,3,4,5]
                         }";

            var dict = JsonConvert.DeserializeObject<Dictionary<object, object>>(json);

            var report = dict["Report"] as JObject;
            var totalCustomerCount = report["TotalCustomerCount"];

            Console.WriteLine($"totalCustomerCount={totalCustomerCount}");

            var arr = dict["CustomerIDHash"] as JArray;
            var list = arr.Select(m => m.Value<int>()).ToList();

            Console.WriteLine($"list={string.Join(",", list)}");
        }

2. 如何讓json中的枚舉保持更易讀的字符串型

這句話是什麼意思呢? 默認情況下, SerializeObject 會將 Model 中的 Enum 變成數值型,大家都知道數值型語義性是非常差的,如下代碼所示:


    static void Main(string[] args)
    {
        var model = new ThreadModel() { ThreadStateEnum = System.Threading.ThreadState.Running };

        var json = JsonConvert.SerializeObject(model);

        Console.WriteLine(json);
    }

    class ThreadModel
    {
        public System.Threading.ThreadState ThreadStateEnum { get; set; }
    }

對吧,確實語義特別差,那能不能直接生成 Running 這種字符串形式呢? 當然可以了。。。改造如下:


  var json = JsonConvert.SerializeObject(model, new StringEnumConverter());

這裏可能就有人鑽牛角尖了,能不能部分指定讓枚舉生成 string,其他的生成 int ,沒關係,這也難不倒我,哪裡使用就用 JsonConverter 標記哪裡。。。


        static void Main(string[] args)
        {
            var model = new ThreadModel()
            {
                ThreadStateEnum = System.Threading.ThreadState.Running,
                TaskStatusEnum = TaskStatus.RanToCompletion
            };

            var json = JsonConvert.SerializeObject(model);

            Console.WriteLine(json);
        }

        class ThreadModel
        {
            public System.Threading.ThreadState ThreadStateEnum { get; set; }

            [JsonConverter(typeof(StringEnumConverter))]
            public TaskStatus TaskStatusEnum { get; set; }
        }        

3. 格式化 json 中的時間類型

在 model 轉化成 json 的過程中,總少不了 時間類型,為了讓時間類型 可讀性更高,通常會 格式化為 YYYY年/MM月/dd日 ,那如何實現呢? 很簡單撒,在 JsonConvert 中也是一個 枚舉 幫你搞定。。。


        static void Main(string[] args)
        {
            var json = JsonConvert.SerializeObject(new Order()
            {
                OrderTitle = "女裝大佬",
                Created = DateTime.Now
            }, new JsonSerializerSettings
            {
                DateFormatString = "yyyy年/MM月/dd日",
            });

            Console.WriteLine(json);
        }
        public class Order
        {
            public string OrderTitle { get; set; }
            public DateTime Created { get; set; }
        }   

對了,我記得很早的時候,C# 自帶了一個 JavaScriptSerializer, 也是用來進行 model 轉 json的,但是它會將 datetime 轉成 時間戳,而不是時間字符串形式,如果你因為特殊原因想通過 JsonConvert 將時間生成時間戳的話,也是可以的, 用 DateFormatHandling.MicrosoftDateFormat 枚舉指定一下即可,如下:

4. 對一些常用設置進行全局化

在之前所有演示的特性技巧中都是在 JsonConvert 上指定的,也就是說 100 個 JsonConvert 我就要指定 100 次,那有沒有類似一次指定,整個進程通用呢? 這麼強大的 Newtonsoft 早就支持啦, 就拿上面的 Order 舉例:


        JsonConvert.DefaultSettings = () =>
        {
            var settings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented
            };
            return settings;
        };

        var order = new Order() { OrderTitle = "女裝大佬", Created = DateTime.Now };

        var json1 = JsonConvert.SerializeObject(order);
        var json2 = JsonConvert.SerializeObject(order);

        Console.WriteLine(json1);
        Console.WriteLine(json2);

可以看到,Formatting.Indented 對兩串 json 都生效了。

5. 如何保證 json 到 model 的嚴謹性 及提取 json 未知字段

有時候我們有這樣的需求,一旦 json 中出現 model 未知的字段,有兩種選擇: 要麼報錯,要麼提取出未知字段,在 Newtonsoft 中默認的情況是忽略,場景大家可以自己找哈。

  • 未知字段報錯

        static void Main(string[] args)
        {
            var json = "{'OrderTitle':'女裝大佬', 'Created':'2020/6/23','Memo':'訂單備註'}";

            var order = JsonConvert.DeserializeObject<Order>(json, new JsonSerializerSettings
            {
                MissingMemberHandling = MissingMemberHandling.Error
            });

            Console.WriteLine(order);
        }

        public class Order
        {
            public string OrderTitle { get; set; }
            public DateTime Created { get; set; }
            public override string ToString()
            {
                return $"OrderTitle={OrderTitle}, Created={Created}";
            }
        }        

  • 提取未知字段

我依稀的記得 WCF 在這種場景下也是使用一個 ExtenstionDataObject 來存儲客戶端傳過來的未知字段,有可能是客戶端的 model 已更新,server端還是舊版本,通常在 json 序列化中也會遇到這種情況,這裏只要使用 JsonExtensionData 特性就可以幫你搞定,在 OnDeserialized 這種AOP方法中進行攔截,如下代碼:


    static void Main(string[] args)
    {
        var json = "{'OrderTitle':'女裝大佬', 'Created':'2020/6/23','Memo':'訂單備註'}";

        var order = JsonConvert.DeserializeObject<Order>(json);

        Console.WriteLine(order);
    }

    public class Order
    {
        public string OrderTitle { get; set; }

        public DateTime Created { get; set; }

        [JsonExtensionData]
        private IDictionary<string, JToken> _additionalData;

        public Order()
        {
            _additionalData = new Dictionary<string, JToken>();
        }

        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            var dict = _additionalData;
        }

        public override string ToString()
        {
            return $"OrderTitle={OrderTitle}, Created={Created}";
        }
    }        

6. 開啟 JsonConvert 詳細日誌功能

有時候在查閱源碼的時候開啟日誌功能更加有利於理解源碼的內部運作,所以這也是一個非常實用的功能,看看如何配置吧。


        static void Main(string[] args)
        {
            var json = "{'OrderTitle':'女裝大佬', 'Created':'2020/6/23','Memo':'訂單備註'}";

            MemoryTraceWriter traceWriter = new MemoryTraceWriter();

            var account = JsonConvert.DeserializeObject<Order>(json, new JsonSerializerSettings
            {
                TraceWriter = traceWriter
            });

            Console.WriteLine(traceWriter.ToString());
        }

        public class Order
        {
            public string OrderTitle { get; set; }

            public DateTime Created { get; set; }

            public override string ToString()
            {
                return $"OrderTitle={OrderTitle}, Created={Created}";
            }
        }

三:總結

嘿嘿,這篇 6 個特性就算說完了, 結合上一篇一共 12 個特性,是不是非常簡單且實用,後面準備給大家帶來一些源碼解讀吧! 希望本篇對您有幫助,謝謝!

如您有更多問題與我互動,掃描下方進來吧~

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

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

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

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

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

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

※回頭車貨運收費標準

誰再悄咪咪的吃掉異常,我上去就是一 JIO

又到周末了,周更选手申請出站~

這次分享一下上個月碰到的離奇的問題。一個簡單的問題,硬是因為異常被悄咪咪吃掉,過關難度直線提升,導致小黑哥排查一個晚上。

這個美好的晚上,本想着開兩把 LOL 無限火力,在召喚師峽谷來個五殺的~

哎,就這樣沒了啊!我知道,你們一定能理解這種五殺被搶的感覺~

下次,真的,誰再讓我看到悄咪咪的吃掉異常,我真的要上去一 Jio 了!

好了,本文可不是水文,看完本篇文章,你可以學到以下知識點:

  • Arthas 排查技巧
  • 啥是 NoClassDefFoundError
  • Dubbo 異常內部處理方式

好了,同學們,打開小本子,準備記好知識點~

先贊后看養成習慣,微信搜索「程序通事」,關注就完事了

起因

我們有個業務系統,應用之間調用鏈如下所示:

A 應用是業務發生起始應用,在這個應用中將會根據一定規則選擇最後的通訊渠道 C,然後將這個渠道標識傳遞給 B 應用。

B 應用的功能類似網關,這個應用將會根據 A 應用傳遞過來的渠道標識,將會請求路由下發到具體的 C 應用,起到服務路由的功能。

C 應用是與外部應用交互的應用,我們將其稱為渠道通訊機。

假設一次業務中,A 應用根據規則選擇 C2 的渠道標識,然後傳遞給 B 應用。B 應用根據這個標識選擇使用 C2 進行通訊,最後 C2 調用外部應用完成一次業務調用。

上述所有應用都基於 Dubbo 進行遠程通訊,B 應用實現原理在小黑哥之前文章「支付路由系統演進史」中有寫過,感興趣的同學可以查看一下。

介紹完業務的基本情況,現在我們來看下到底發生了啥事。

一次業務需求中,需要改動 C2 應用,這次改動功能點真的很小,很快就完成了。小黑哥想着閑着也是閑着,於是就把之前 C2 應用中打印的日誌中一些沒有脫敏的信息,進行脫敏處理。

由於之前日誌框架脫敏處理存在一些問題,於是就將日誌框架從 Log4j 升級為 LogBack。升級之後,為了防止不同日誌框架中之間的產生衝突,於是使用 IDEA Maven Helper 插件,統一將應用中所有的 Log4j 相關依賴都給排除了。

改動完成之後,將 C2 應用發布到測試環境,再次從 A 應用發起測試, B 應用返回異常提示未找到 C2 應用

B 應用業務代碼類似如下:

public Response pay(Request req) {
    
    try {
        if (!isSupport(req.getChnlCode())) {
            return new Response("ERROR", "未找到相關渠道應用");
        }
        return doPay(req);
    } catch (Exception e) {
        return new Response("ERROR", "未找到相關渠道應用");
    }
}

正常情況下,若是配置存在問題,B 應用將會返回未找到具體渠道,請求也會在 B 應用結束,不會調用到 C2 應用(也沒辦法調用)。

然而此次配置什麼都沒問題, 而且最詭異的是 C2 應用居然收到了請求,並且成功處理了業務請求。

排查問題

由於 B 應用異常處理時,將異常吃掉了,我們沒辦法得知這個過程到底發生了啥事,所以第一要緊的事獲取異常信息。

最簡單的辦法就是,將 B 應用改造一下,加入打印異常日誌。不過當時比較懶,不想改造應用,就想獲取異常信息,於是想到使用 Arthas

Arthas 排錯技巧

Arthas 是Alibaba開源的Java診斷工具,這裏就不再詳細介紹這個工具,主要講下這次排錯用到的命令-watch

watch 命令可以方便觀察指定方的調用情況,可以具體觀察方法的返回值拋出異常入參,另外還可以通過 OGNL表達式查看對應的變量。

這裏我們主要為了查看方法拋出的異常信息,執行命令如下:

watch com.dubbo.example.DemoService doPay -e -x 2 '{params,throwExp}'

上述命令將會在方法異常之後觀察方法的入參以及異常信息。

注意,我們需要查看 doPay 方法,而不是 pay 方法。這是因為 pay方法中我們將異常捕獲,不太可能會拋出異常哦~

異常信息如下所示:

真正引起此次錯誤的異常信息為:

java.lang.NoClassDefFoundError: Could not initialize class xx.xxx.xx.GELogger

由於此次 B 應用不存在改動,所以推測這個異常實際發生在 C2 應用,於是在 C2 應用處再次使用 Arthas watch 命令,同樣觀察到相同的錯誤信息。

NoClassDefFoundError

NoClassDefFound,從名字上我們可以推測是因為類不存在,從而引發的這個錯誤。按照這個思路,我們首先可以簡單查看一下 B 應用中是否存在 GELogger 相關類。

查看 B 應用相關依賴包,從中發現了這個類文件,這說明這個類確實存在。

在 IDEA 反編譯查看 GELogger類相關源碼,從中發現了問題。

private static Logger logger;

static {
    System.out.println("static init");
    logger = Logger.getLogger(NoClassDefFoundErrorTestService.class);
    System.out.println("Logger init success");
}

GELogger存在一個靜態代碼塊,用於初始化一個 org.apache.log4j.Logger日誌類。

然後在上面改動中,全部的 Log4j依賴都被排除了,所以這裏運行時應該會拋出另外一個找到 org.apache.log4j.Logger 錯誤。

執行以下代碼,模擬拋錯過程。

System.out.println("模擬第一次 Error");
try {
    NoClassDefFoundErrorTestService noClassDefFoundErrorTestService=new NoClassDefFoundErrorTestService();
} catch (Throwable e) {
    e.printStackTrace();
}
System.out.println("模擬第二次 Error");
try {
    NoClassDefFoundErrorTestService noClassDefFoundErrorTestService=new NoClassDefFoundErrorTestService();
} catch (Throwable e) {
    e.printStackTrace();
}

異常信息如下所示:

第一次創建 NoClassDefFoundErrorTestService實例時,Java 虛擬機讀取加載時,將會初始化靜態代碼塊時。由於 org.apache.log4j.Logger類不存在,靜態代碼塊執行異常,從而導致類加載失敗。

第二次再創建 NoClassDefFoundErrorTestService 實例時,Java 虛擬機不會再次讀取加載,所以直接返回了以下異常。

java.lang.NoClassDefFoundError: Could not initialize class com.dubbo.example.NoClassDefFoundErrorTestService

找到問題真正原因,解決辦法也很簡單,直接排除 GELogger 所在依賴包。

Dubbo 內部異常處理

雖然問題到此解決了,但是這裏還有一個疑問,為何 C2 應用發生了異常,卻沒有相關錯誤日誌,並且 C2 業務邏輯也正常處理完成。

這就要說到 Dubbo 內部異常錯誤處理方式,上面 GELogger 其實作用在一個 Dubbo 自定義 Filter 中,用來記錄結果,模擬代碼如下:

@Activate(
        group = {"provider", "consumer"}
)
public class ErrorFilter implements Filter {


    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

        Result result = invoker.invoke(invocation);
        NoClassDefFoundErrorTestService noClassDefFoundErrorTestService=new NoClassDefFoundErrorTestService();
        // 處理業務邏輯
        return result;
    }
}

這個自定義 Filter 中首先執行 invoker 方法,這個方法將會調用真正的業務方法,這就是為什麼 C2 應用邏輯是正常處理完成。

業務方法處理完成之後,然後執行後續邏輯。由於 NoClassDefFoundErrorTestService將會拋出 Error,最終這個 Error,將會在 HeaderExchangeHandler#handleRequest 被捕獲,然後將會把相關異常信息返回給調用 Dubbo 消費者。

而在 Dubbo 消費者接受到服務提供者返回信息之後,將會在 DefaultFuture#doReceived轉化成 RemotingException

RemotingException 最終將會在 FailoverClusterInvoker#doInvoke 轉換成 RpcException返回給業務代碼。

總結

好了,說了這麼多,總結一下本文知識點

  1. 異常捕獲之後,一定要記得打印日誌,並且要記得輸出堆棧信息
  2. 運行時類不存在,將會導致 NoClassDefFoundError,類加載過程失敗,也會導致 NoClassDefFoundError
  3. 對外提供的二方包,最好不要依賴特定日誌框架,如 Log4j,Logback 等,應該使用 Slf4j 框架。

幫助

1、當Dubbo遇上Arthas:排查問題的實踐

2、java.lang.NoClassDefFoundError 的解決方法一例

3、noclassdeffounderror-could-not-initialize-class-error

歡迎關注我的公眾號:程序通事,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關注我的博客:studyidea.cn

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案